Neverthrow Exception Wrapping
Goal
Capture recoverable exceptions with neverthrow helpers instead of ad hoc try/catch.
This skill governs exception capture only. If the task also changes public return signatures, use neverthrow-return-types alongside this skill.
Detect Exception Sources
Identify where failures currently enter the code.
- Look for hand-written
try/catch, .catch(...) wrappers used only for conversion, direct calls to known throwing APIs, and promise-returning functions that may reject.
- Check third-party libraries, parsers, database clients, network clients, file-system helpers, schema validators, and serialization code.
Distinguish the failure shape before choosing a wrapper.
- Use the synchronous path when the operation may throw before returning a value.
- Use the promise-function path when the operation returns a promise but may still throw before that promise exists.
- Use the promise-instance path when you already have a
PromiseLike value in hand.
Do not wrap APIs that already return Result or ResultAsync.
- Compose them directly with
map, mapErr, andThen, asyncAndThen, or orElse.
Choose the Wrapper
Use Result.fromThrowable or fromThrowable for synchronous throwing functions.
- Always pass an error mapper so the
Err side has a known type.
Use ResultAsync.fromThrowable for promise-returning functions that can throw before returning or fail during async execution.
- Prefer this over
ResultAsync.fromPromise(fn(...), ...) when the function call itself might throw.
Use ResultAsync.fromPromise or fromPromise when you already have a PromiseLike value.
- Map rejected values into a concrete error type immediately.
Reuse narrow mapper functions when the same error shape appears repeatedly.
- Prefer stable domain errors over
unknown, any, and generic strings.
Avoid try/catch by Default
Do not add new hand-written try/catch blocks when a neverthrow helper fits the job.
- Extract the risky operation into a function if needed and wrap that function.
Keep try/catch only when the surrounding construct truly requires it.
- Examples include cleanup flows that need
finally, framework boundaries that must intercept and translate exceptions, or language constructs that cannot be expressed cleanly with wrapper helpers alone.
If try/catch remains necessary, keep it at the narrowest boundary.
- Convert the caught value into
Err or the required framework-native response immediately.
- Do not let the caught value flow through the codebase as untyped
unknown.
Implementation Rules
Wrap once near the source of the throwable or rejecting operation.
- Avoid nested wrappers around the same operation.
Keep error mapping explicit.
- Prefer mapper functions that preserve useful context such as operation name, input identifiers, or upstream status codes when the local style allows it.
Replace conversion-only .catch(...) chains when neverthrow provides a clearer wrapper.
- Do not simulate
ResultAsync manually with Promise.resolve, Promise.reject, or custom wrapper objects.
Example Patterns
const parseConfig = Result.fromThrowable(
JSON.parse,
(error) => ({ type: 'ConfigParseError', cause: error }),
)
const fetchUser = ResultAsync.fromThrowable(
apiClient.getUser,
(error) => ({ type: 'UserFetchError', cause: error }),
)
function readBody(): ResultAsync<RequestBody, BodyReadError> {
return ResultAsync.fromPromise(request.json(), toBodyReadError)
}
Validate Before Finishing
- Verify new or edited failure capture uses
neverthrow helpers where applicable.
- Verify each wrapper choice matches the real failure shape: synchronous throw, promise-returning function, or existing promise.
- Verify all error mappers produce explicit error types.
- Verify any remaining
try/catch block is documented by a real constraint instead of habit.
- Run the normal local validation for the stack when it is safe and in scope, such as tests, linting, or type checks.
Report the Outcome
When finishing the task:
- State which throwing or rejecting operations were wrapped.
- State which
neverthrow helper was used and why.
- State any remaining
try/catch blocks and why they were unavoidable.
- State how caught or rejected values are mapped into explicit error types.
Source: code-sherpas/agent-skills — distributed by TomeVault.
1---2name: neverthrow-wrap-exceptions3description: Capture exceptions and promise failures with `neverthrow` instead of hand-written `try/catch` in TypeScript and JavaScript code. Use when wrapping synchronous functions that may throw, promise-returning functions that may throw before returning, existing `PromiseLike` values that may reject, or third-party APIs such as parsers, database clients, HTTP clients, file-system helpers, serializers, and SDK calls. Prefer `Result.fromThrowable` for synchronous throwers, `ResultAsync.fromThrowable` for promise-returning functions that may throw or reject, and `ResultAsync.fromPromise` when you already have a `PromiseLike` value in hand. Only keep `try/catch` when the language construct, cleanup requirement, or framework boundary truly requires it. Use when this capability is needed.4---56# Neverthrow Exception Wrapping78## Goal910Capture recoverable exceptions with `neverthrow` helpers instead of ad hoc `try/catch`.1112This skill governs exception capture only. If the task also changes public return signatures, use `neverthrow-return-types` alongside this skill.1314## Detect Exception Sources15161. Identify where failures currently enter the code.17 - Look for hand-written `try/catch`, `.catch(...)` wrappers used only for conversion, direct calls to known throwing APIs, and promise-returning functions that may reject.18 - Check third-party libraries, parsers, database clients, network clients, file-system helpers, schema validators, and serialization code.19202. Distinguish the failure shape before choosing a wrapper.21 - Use the synchronous path when the operation may throw before returning a value.22 - Use the promise-function path when the operation returns a promise but may still throw before that promise exists.23 - Use the promise-instance path when you already have a `PromiseLike` value in hand.24253. Do not wrap APIs that already return `Result` or `ResultAsync`.26 - Compose them directly with `map`, `mapErr`, `andThen`, `asyncAndThen`, or `orElse`.2728## Choose the Wrapper29301. Use `Result.fromThrowable` or `fromThrowable` for synchronous throwing functions.31 - Always pass an error mapper so the `Err` side has a known type.32332. Use `ResultAsync.fromThrowable` for promise-returning functions that can throw before returning or fail during async execution.34 - Prefer this over `ResultAsync.fromPromise(fn(...), ...)` when the function call itself might throw.35363. Use `ResultAsync.fromPromise` or `fromPromise` when you already have a `PromiseLike` value.37 - Map rejected values into a concrete error type immediately.38394. Reuse narrow mapper functions when the same error shape appears repeatedly.40 - Prefer stable domain errors over `unknown`, `any`, and generic strings.4142## Avoid try/catch by Default43441. Do not add new hand-written `try/catch` blocks when a `neverthrow` helper fits the job.45 - Extract the risky operation into a function if needed and wrap that function.46472. Keep `try/catch` only when the surrounding construct truly requires it.48 - Examples include cleanup flows that need `finally`, framework boundaries that must intercept and translate exceptions, or language constructs that cannot be expressed cleanly with wrapper helpers alone.49503. If `try/catch` remains necessary, keep it at the narrowest boundary.51 - Convert the caught value into `Err` or the required framework-native response immediately.52 - Do not let the caught value flow through the codebase as untyped `unknown`.5354## Implementation Rules55561. Wrap once near the source of the throwable or rejecting operation.57 - Avoid nested wrappers around the same operation.58592. Keep error mapping explicit.60 - Prefer mapper functions that preserve useful context such as operation name, input identifiers, or upstream status codes when the local style allows it.61623. Replace conversion-only `.catch(...)` chains when `neverthrow` provides a clearer wrapper.63 - Do not simulate `ResultAsync` manually with `Promise.resolve`, `Promise.reject`, or custom wrapper objects.6465## Example Patterns6667```ts68const parseConfig = Result.fromThrowable(69 JSON.parse,70 (error) => ({ type: 'ConfigParseError', cause: error }),71)7273const fetchUser = ResultAsync.fromThrowable(74 apiClient.getUser,75 (error) => ({ type: 'UserFetchError', cause: error }),76)7778function readBody(): ResultAsync<RequestBody, BodyReadError> {79 return ResultAsync.fromPromise(request.json(), toBodyReadError)80}81```8283## Validate Before Finishing84851. Verify new or edited failure capture uses `neverthrow` helpers where applicable.862. Verify each wrapper choice matches the real failure shape: synchronous throw, promise-returning function, or existing promise.873. Verify all error mappers produce explicit error types.884. Verify any remaining `try/catch` block is documented by a real constraint instead of habit.895. Run the normal local validation for the stack when it is safe and in scope, such as tests, linting, or type checks.9091## Report the Outcome9293When finishing the task:9495- State which throwing or rejecting operations were wrapped.96- State which `neverthrow` helper was used and why.97- State any remaining `try/catch` blocks and why they were unavoidable.98- State how caught or rejected values are mapped into explicit error types.99100---101> Source: [code-sherpas/agent-skills](https://github.com/code-sherpas/agent-skills) — distributed by [TomeVault](https://tomevault.io).102<!-- tomevault:4.0:skill_md:2026-06-15 -->