neverthrow-wrap-exceptions
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.
- Look for hand-written
-
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
PromiseLikevalue in hand.
-
Do not wrap APIs that already return
ResultorResultAsync.- Compose them directly with
map,mapErr,andThen,asyncAndThen, ororElse.
- Compose them directly with
Choose the Wrapper
-
Use
Result.fromThrowableorfromThrowablefor synchronous throwing functions.- Always pass an error mapper so the
Errside has a known type.
- Always pass an error mapper so the
-
Use
ResultAsync.fromThrowablefor 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.
- Prefer this over
-
Use
ResultAsync.fromPromiseorfromPromisewhen you already have aPromiseLikevalue.- 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.
- Prefer stable domain errors over
Avoid try/catch by Default
-
Do not add new hand-written
try/catchblocks when aneverthrowhelper fits the job.- Extract the risky operation into a function if needed and wrap that function.
-
Keep
try/catchonly 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.
- Examples include cleanup flows that need
-
If
try/catchremains necessary, keep it at the narrowest boundary.- Convert the caught value into
Error the required framework-native response immediately. - Do not let the caught value flow through the codebase as untyped
unknown.
- Convert the caught value into
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 whenneverthrowprovides a clearer wrapper.- Do not simulate
ResultAsyncmanually withPromise.resolve,Promise.reject, or custom wrapper objects.
- Do not simulate
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
neverthrowhelpers 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/catchblock 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
neverthrowhelper was used and why. - State any remaining
try/catchblocks and why they were unavoidable. - State how caught or rejected values are mapped into explicit error types.
More from code-sherpas/agent-skills
neverthrow-return-types
Require `neverthrow`-based return types in TypeScript and JavaScript code whenever the surrounding technology allows it. Use when creating, refactoring, reviewing, or extending standalone functions, exported module functions, class methods, object methods, service methods, repository methods, and similar APIs that should expose explicit success and failure result types in their signatures. Prefer `Result<T, E>` for synchronous code and `ResultAsync<T, E>` for asynchronous code. Only skip a `neverthrow` return type when a framework, library, runtime interface, or externally imposed contract is incompatible and requires a different return shape.
16atomic-design
Create or update web UI components with a strict reuse-first workflow. Use when building, refactoring, restyling, or extending frontend or template components while minimizing raw DOM or HTML by reusing or generalizing existing components first.
10write-persistence-representations
Create or update persistence-layer data representations in any stack, including ORM entities, schema definitions, table mappings, document models, collection definitions, and similar database-facing code. Use when agents needs to add or change persisted fields, identifiers, relationships, indexes, timestamps, auditing fields, or storage mappings in frameworks, libraries, or ORMs such as Prisma, TypeORM, Sequelize, Drizzle, Mongoose, Hibernate/JPA, Doctrine, Ecto, Active Record, or equivalent persistence technologies.
7business-logic
Identify, interpret, review, or write business logic in code. Use when an agent needs to decide whether code expresses business rules, business algorithms, or business workflows, or when it must implement, preserve, or refactor code that creates, stores, or transforms data according to real business policies.
7immutable-domain-entities
Require the immutable design pattern for domain entities. Use when an agent needs to create, modify, review, or interpret domain entities and should preserve identity while expressing state changes through new immutable instances. Domain entities must be modeled as immutable classes, not as plain type aliases or interfaces paired with standalone functions.
7update-agent-skills
Update agent skills installed with the `skills` CLI. Use when asked to refresh installed skills, keep a project's skills current, or troubleshoot cases where `npx skills update` reports that everything is up to date. For project-scoped installs, a no-change update must immediately run the bundled reinstall script so tracked skills from `skills-lock.json` are reinstalled without extra investigation.
7