Error Handling Conventions
SkillCloud & infraApply when handling errors or logging in components, composables, stores, server routes, tRPC routers, or Azure Functions handlers. Esposter's error handling, neverthrow through getResult/getResultAsync with try and .then banned, every Result terminated with .match, a failure logged or shown and never swallowed, InvalidOperationError over new Error, and the tRPC guards (requireEntity, requireMutation) over a hand-rolled null check.
Use Error Handling Conventions in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Error Handling Conventions and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Error Handling Conventions skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by esposter/esposter in .agents/skills/error-handling/SKILL.md and read by Ahel’s review.
neverthrow for explicit error handling. No silent swallows — every error is propagated, logged, or shown to the user.
Settled — do not re-propose
- A rule for an unterminated
Result— needs the value's type, and no type-aware rule of our own runs in either linter (theoxlintskill, "No type-aware rule of our own runs in either linter");pnpm ai:sweep:unterminated-resultsreads the code after the bracket instead, so it stays a scan a sitting runs. - A rule for a fire-and-forget body that does not terminate — whether the body has anything to terminate is a question about what it calls: a body whose whole work is an
executeMutationwith anonErroris already done. - Reaching a template's inline alert from
error-alert/no-raw-error-alert— oxlint hands a JS plugin no Vue template. - Banning
console.warnoutright — a notice with its own sentence is allowed; the handler slot is the swallow, and that half is the selector.
Deep dives
references/result-chains.md— when shaping one chain: a fallback value, an alert, a mid-chain side effect, aninstanceofbranch on the error, or an abort/cancel.references/alerting.md— when wiring the error path of a tRPC call, or a background read that must not alert.references/finalizers.md— when a chain has to release something whichever way it resolves:withFinalizerorwithFinalizerAsync, and when neither is the shape.references/server-guards.md— when a tRPC router or server route guards a nullable DB result, attaches acauseto aTRPCError, or has a fire-and-forget tail on a path a caller rolls back.references/ban-exceptions.md— when a callback must become a rejection, a throw is being kept synchronous for a test, or a.then/.catch/.finallylooks unavoidable.references/azure-functions.md— when writing or changing an Azure Functions handler, its dead-letter replay, or a handler that enumerates its own work from a query.references/throwing.md— when a code path throws, or a mock stubs a vendor method it never serves.references/json-parsing.md— when parsing JSON: user input, a stored blob, or a round trip carrying dates.references/error-classes.md— when writing or deduplicating an error class.references/wrapping.md— when deciding whether a step gets aResult, or whether a chain is terminated.references/logging-sinks.md— when writing an err handler or a notice: the sink per runtime, and whereconsole.warnstays.references/no-shared-import.md— when handling a rejection in code that cannot import@esposter/shared.references/unawaited-callbacks.md— when writing a tick, a timer, a fire-and-forget hook or a gating promise executor.
try / catch and .then Are BANNED
try in any form and .then/.catch/.finally are no-restricted-syntax errors — getResult/getResultAsync and a chain, withFinalizer/withFinalizerAsync for cleanup; the two disable shapes and Promise.try are references/ban-exceptions.md.
Throwing — never new Error
- Never
new Error(...)— throwInvalidOperationError(operation, name, message)(error-handling/no-bare-error); the exceptions arereferences/throwing.md. - JSON is parsed by a schema or by
jsonDateParse, never a bareJSON.parsewith a cast — which one a path takes isreferences/json-parsing.md.
Core Utility
import { getResult, getResultAsync, noop, withFinalizer, withFinalizerAsync } from "@esposter/shared";
// getResult: sync fn → Result<T, Error>
// getResultAsync: async fn → ResultAsync<T, Error>
// noop: () => {} — the ok-handler in .match(noop, errorHandler)
// withFinalizer: sync fn + sync finalizer (e.g. restoring globals)
// withFinalizerAsync: async/sync fn + async/sync finalizer — for all async operations
- Always use
getResult(() => expr)/getResultAsync(() => asyncExpr)— neverthrow'sfromThrowable/fromPromisecalled directly is ano-restricted-syntaxerror, disabled only where@esposter/sharedcannot be imported (references/no-shared-import.md). - Each error class writes
this.nameas a literal, nevernew.target.name, which the minifier mangles (references/error-classes.md). - Wrap only what can actually fail — a pure step is called bare (
references/wrapping.md). - Never leave a
Resultunterminated —.match,.unwrapOror._unsafeUnwrap(); no lint rule can see it, so it is a review catch andpnpm ai:sweep:unterminated-resultsa scan (references/wrapping.md). .isOk()/.isErr()are BANNED (no-restricted-syntax) — branch with.match(...)instead so both branches are handled in one place. To rethrow/cleanup on failure,throwinside the err handler (works in sync and async handlers alike); to fall back,.unwrapOr(fallback).- Never a silent swallow, and never
console.warnas an err handler —.orTee(console.error),context.errorin an Azure Function,writeVirrunDebugin virrun (references/logging-sinks.md). .match(noop, noop)is a silent swallow — a best-effort err handler names what was lost (no-restricted-syntax,references/logging-sinks.md).- Never
voida ResultAsync — alwaysawait(ResultAsync never rejects, so awaiting is safe). - Code that cannot import
@esposter/shared— a package a stranger installs alone, a config loaded before any package builds — handles a rejection without it (references/no-shared-import.md). - Never end a fire-and-forget chain with
.orTee(handler)alone — use.match(noop, handler). Only the async form is caught: aResultAsyncis a thenable, so a bare one tripstypescript/no-floating-promises, while a syncgetResult(...).orTee(...)statement trips nothing and is a review catch. - No-op ok handler: always
noop— an inline() => {}, or a() => undefinedwhose match value is discarded, is ano-restricted-syntaxerror. Assigned,() => undefinedis the value the ok arm produces and stays. - A callback nothing awaits terminates its own
Resultinside its own body, and a promise executor's err handler also resolves its gate (references/unawaited-callbacks.md).
Who alerts a tRPC rejection — references/alerting.md
errorLink alerts some rejection codes itself, so a caller that alerts them again stacks two identical toasts on one failure. Wiring the error path of a tRPC call, or a background read that must not alert at all, is that page.
Finalizers — references/finalizers.md
withFinalizer and withFinalizerAsync run cleanup either way and then unwrap, so no terminal consumer is needed. Releasing something a chain acquired, whichever way it resolves, is that page.
tRPC Backend Guards
Routers never repeat a null check: requireEntity and requireMutation from server/trpc/guards/, and a rejection asserted with the same family's error constructors (references/server-guards.md).
Client Reads/Writes — Don't Hand-Roll the Chain
useQuery / useMutation already carry this chain for client reads/writes — see the trpc skill (references/client-calls.md) before writing your own around a $trpc call.
Signals
- GitHub stars
- 23
- Forks
- 3
- Last commit
- Oct 2026
- Hacker News mentions
- 4
Advanced
- Item type
- skill
- Key
error-handling-esposter- Source
- github.com/esposter/esposter
Related picks
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScriptnodejs-backend-patterns
Skill · wshobson
The pick for Noderun-node-tests
Skill · hiroro-work
The pick for Nodesw-vue-part
Skill · onweekendd
The pick for Vuevue-composable-patterns
Skill · esposter
The pick for Vue