api-pagination
Implement correct, fast API pagination — cursor vs offset trade-offs, opaque cursor encoding, stable sort keys, page-size limits, total-count costs, and…
How to handle errors explicitly and consistently across an app — validate at boundaries, classify operational vs programmer errors, add context while propagating, retry transient failures with backoff, and never swallow. Covers JS/Python/Go/Rust patterns with runnable checks.
$ npx -y skills add vanara-agents/skills --skill error-handling-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/error-handling-patternsContext preview
The summary Claude sees to decide when to auto-load this skill.
How to handle errors explicitly and consistently across an app — validate at boundaries, classify operational vs programmer errors, add context while propagating, retry transient failures with backoff, and never swallow. Covers JS/Python/Go/Rust patterns with runnable checks.
name: error-handling-patterns description: How to handle errors explicitly and consistently across an app — validate at boundaries, classify operational vs programmer errors, add context while propagating, retry transient failures with backoff, and never swallow. Covers JS/Python/Go/Rust patterns with runnable checks. type: skill version: 2.0.0 updated: 2026-06-29
Errors are part of the contract, not an afterthought. A robust system **handles errors where it can act, propagates them where it can't, and never lets a failure vanish silently**. This skill is the deep reference: the decisions, the trade-offs, and the anti-patterns. Heavy detail lives in `references/`, copy-paste material in `examples/`, and a runnable linter for swallowed errors in `scripts/`.
Every error sits on two axes that decide what you do with it:
| Axis | Question | Consequence | |---|---|---| | **Operational vs programmer** | Is this an expected runtime condition (network down, bad input) or a bug (null deref, broken invariant)? | Operational → recover/retry/surface. Programmer → fail fast, let it crash, fix the code. | | **Recoverable vs fatal** | Can the caller do something useful about it here? | Yes → handle locally. No → add context and propagate. |
Classifying wrong is the root cause of most bad error handling: retrying a programmer bug forever, or crashing the process over a single bad HTTP request. The full taxonomy is in [`references/error-taxonomy.md`](references/error-taxonomy.md).
1. **Validate at the boundary.** Reject bad input early with a clear, specific message (fail fast). Never trust data crossing a trust boundary — HTTP bodies, env vars, file contents, API responses. 2. **Classify the error.** Operational errors are values you handle; programmer errors are bugs you surface loudly. Don't `catch` a `TypeError` to "be safe". 3. **Add context as you propagate.** Wrap with *what you were doing* and preserve the original cause and stack (`new Error(msg, { cause })` in JS, `fmt.Errorf("...: %w", err)` in Go). 4. **One owner for user-facing messaging.** Translate to a friendly message at the edge (the HTTP layer, the UI). Keep full detail in server-side logs only — never leak stack traces to users. 5. **Never swallow.** An empty `catch {}`, a bare `except: pass`, or a `.catch(() => {})` is a defect. Run `scripts/lint-empty-catch.mjs` in CI to catch these automatically.
The single most valuable habit: wrap errors with context while chaining the original, so the final log shows the full causal trail from the low-level failure up to the request that triggered it.
// Node 16.9+ supports the standard `cause` option on Error.
async function placeOrder(userId, cart) {
try {
const order = await db.orders.insert({ userId, items: cart.items });
return order;
} catch (err) {
// Wrap: keep the DB error as `cause`, add the business context.
throw new Error(`failed to place order for user ${userId}`, { cause: err });
}
}# Python's `raise ... from` preserves the chain (shown as "The above exception
# was the direct cause of the following exception" in the traceback).
def place_order(user_id, cart):
try:
return db.orders.insert(user_id=user_id, items=cart.items)
except DBError as err:
raise OrderError(f"failed to place order for user {user_id}") from errLanguage-by-language patterns (try/catch vs `Result`/`Either` vs panics) are in [`references/language-patterns.md`](references/language-patterns.md).
Only **operational, transient, idempotent** failures are safe to retry — a 503 or a connection reset, not a 400 or a `NullPointerException`. Retry with **exponential backoff plus full jitter** to avoid synchronized retry storms (the "thundering herd"), and always cap attempts and total time.
async function withRetry(fn, { retries = 4, baseMs = 100, isRetryable } = {}) {
let attempt = 0;
for (;;) {
try {
return await fn();
} catch (err) {
attempt++;
if (attempt > retries || (isRetryable && !isRetryable(err))) throw err;
const backoff = baseMs * 2 ** (attempt - 1);
const jittered = Math.random() * backoff; // full jitter
await new Promise((r) => setTimeout(r, jittered));
}
}
}When NOT to retry, circuit breakers, and budgets are covered in [`references/retry-and-backoff.md`](references/retry-and-backoff.md).
Go. The failure disappears and you debug blind. This is the #1 defect; lint for it.
error logged ten times. Log once, at the boundary that owns the response.
programmer bugs, or `OutOfMemory`. Catch the narrowest type you can actually handle.
caller to remember the magic check; most won't. Prefer throwing or a `Result` type.
leak (reveals schema, file paths, library versions).
double-charge; retrying a `400` just wastes time. See the retry reference.
that tells you *why*.
(let a supervisor like systemd/k8s/PM2 restart it cl
🐒 Free agents, skills & packs for Claude Code One subscription. An army of Claude Code agents. 30 production-grade agents, skills, and packs for Claude Code — free, Apache-2.0, install with one command.
Repo: vanara-agents/skills
Implement correct, fast API pagination — cursor vs offset trade-offs, opaque cursor encoding, stable sort keys, page-size limits, total-count costs, and…
Deep reference for caching — what to cache, cache-aside vs read/write-through/write-behind, TTLs with jitter, eviction (LRU/LFU/FIFO), invalidation, and…
Write Conventional Commits — the type(scope)!: subject + body + footer spec — so history is readable and changelogs and SemVer bumps can be derived…
How to write safe, reversible, zero-downtime database schema migrations — additive-first changes, the expand/migrate/contract pattern, batched backfills,…
Run git collaboration that scales — trunk-based vs git-flow decided by deploy cadence, branch protection and required checks, PR sizing and review etiquette,…
A deep prevention reference for the OWASP Top 10 web risks — broken access control, injection, crypto failures, insecure design, SSRF and more — with…