error-handling
Error Handling
Error classification
- Programming errors: Bugs (TypeError, ReferenceError, assertion failures, etc.). Fix the code; do not catch and continue
- Operational errors: Expected failures (network timeout, file not found, invalid input, etc.). Handle gracefully with recovery, escalation, user notification, or logging
Sync error handling
try {
const result = parse(input);
return result;
} catch (error) {
console.error({ error });
return defaultValue;
}
Async error handling
try {
const data = await fetchData(url);
return data;
} catch (error) {
if (error.code === 'ECONNREFUSED') return fallback();
throw err;
}
DomainError
Structured business errors with codes for the API layer:
({
method: async ({ email }) => {
const user = await domain.user.findByEmail(email);
if (!user) return new DomainError('ENOTFOUND');
return user;
},
errors: { ENOTFOUND: 'User not found' },
});
Domain layer throws plain errors; API layer translates to DomainError when needed.
Error propagation across layers
domain throws Error → API catches → returns DomainError(code) → client gets structured error
Do not expose internal error details to clients; map to known error codes or general errors.
Conventions
- Distinguish programmer vs operational errors; only recover from operational
- Use DomainError for business validation in API methods; throw for programming bugs
- Never swallow errors silently; always log or propagate
- Implement graceful shutdown: stop accepting connections, drain existing, release resources
- Use retry with exponential backoff for transient failures (network, DB connections)
- Always handle both
uncaughtExceptionandunhandledRejectionat process level
Retry pattern
const retry = async (fn, { attempts = 3, delay = 1000 } = {}) => {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, delay * (i + 1)));
}
}
};
Process-level handlers
process.on('uncaughtException', (error) => {
console.error('Uncaught:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error('Unhandled rejection:', reason);
});
Log and exit on uncaughtException (state may be corrupted). Log unhandledRejection and consider it a bug to fix.
Graceful shutdown
Track connections and drain before exit:
const connections = new Map();
server.on('connection', (conn) => {
const res = null;
connections.set(conn, res);
conn.on('close', () => connections.delete(conn));
});
const shutdown = () => {
server.close(() => {
freeResources();
process.exit(0);
});
for (const [conn, res] of connections) {
if (res) res.end('Server shutting down');
conn.destroy();
}
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
More from metarhia/skills
javascript-code-style
Apply Metarhia JavaScript style. Use when writing or editing .js, .mjs, .ts files (with certain corrections for typescript), formatting code, or when the user asks about code style or linting.
5js-gof
Apply Gang of Four and related design patterns in JavaScript and TypeScript. Use when implementing creational, structural, or behavioral patterns, or when the user mentions factories, builder, prototype, flyweight, singleton, object pool, adapter, wrapper, decorator, proxy, bridge, composite, facade, chain of responsibility, command, interpreter, iterator, mediator, memento, EventEmitter, EventTarget, state, strategy, template method, visitor, revealing constructor, actor, service locator, or any other pattern.
2js-conventions
Apply Metarhia JavaScript style. Use when writing or editing .js, .mjs, .ts files (with certain corrections for typescript), formatting code, or when the user asks about code style or linting.
2npm-publish
Prepare an npm package for publishing. Handles version bump, CHANGELOG update, typings check, test run, README refresh, package.json audit, and npm pack verification. Use when the user asks to prepare a release, bump version, publish a package, or do release prep.
1js-data-structures
Implement and use data structures in JavaScript. Use when working with arrays, sets, maps, other collections, queues, lists, trees, graphs, circular buffers, caches, or when the user mentions data structures, BTree, or choosing between Map/Object/Set/Array.
1