Prevent Swallowed Errors and Empty Catch Blocks in TypeScript
Flags empty catch blocks, catch-all fallbacks that return null, rethrows without a cause, and non-Error throws that hide failures in TypeScript code.
What it flags
import { billingClient } from './billing-client';
import { parseInvoice, type Invoice } from './invoice';
export async function loadInvoice(id: string): Promise<Invoice | null> {
try {
const res = await billingClient.get(`/invoices/${id}`);
return parseInvoice(res.body);
} catch {
// outages and parsing bugs now look like "invoice not found"
return null;
}
}import { billingClient, HttpError } from './billing-client';
import { parseInvoice, type Invoice } from './invoice';
export async function loadInvoice(id: string): Promise<Invoice | null> {
try {
const res = await billingClient.get(`/invoices/${id}`);
return parseInvoice(res.body);
} catch (err) {
if (err instanceof HttpError && err.status === 404) {
return null;
}
throw new Error(`Failed to load invoice ${id}`, { cause: err });
}
}Why it matters
A catch that returns null turns a database outage or a parsing bug into an empty result, so the failure shows up as missing data with nothing in the logs. Rethrowing without a cause drops the original stack, and throwing a string provides no stack at all, which makes incidents slower to trace. These catches are often added to make a type error or a flaky test go away, so they read as defensive code in review.
Agent instructions
cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.
AGENTS.md
Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.
## Prevent Swallowed Errors and Empty Catch Blocks in TypeScript
Applies to `**/*.ts`, `**/*.tsx`, `**/*.mts`, `**/*.cts`.
Review TypeScript error handling for failures that are hidden, lose their original cause, or cannot be inspected by callers.
Flag changes that:
- Add `catch` blocks that contain only a placeholder comment such as `// ignore`, which the `no-empty` lint rule accepts, or promise chains ending in `.catch(() => {})` or `.catch(() => undefined)`.
- Return `null`, `undefined`, `[]`, `false`, or another default from a `catch`, so callers cannot tell an outage or a bug from a normal empty result.
- Catch every error when only one is expected, such as treating any failure as "not found" instead of checking for a 404 status or a specific error class.
- Throw a new error inside `catch` without the original as its `cause`: `throw new Error(message)` drops the original stack, while `throw new Error(message, { cause: err })` keeps it.
- Throw or reject with strings, plain objects, or other non-`Error` values, which carry no stack trace and fail `instanceof Error` checks.
- Read `(err as Error).message` on a caught value instead of narrowing with `err instanceof Error`, even though any value can be thrown.
Do not flag:
- Best-effort work where failure is expected and harmless, such as deleting a temp file or sending analytics, when the code logs the error or comments why it is ignored.
- Catches that ignore one specific, checked error, such as an `AbortError` from a canceled request.
- Catches that convert the error into a typed result such as `{ ok: false, error }` that callers must handle.
Checks files matching **/*.ts, **/*.tsx, **/*.mts, **/*.cts.
More TypeScript agents
See all TypeScript code review standards
Run this agent on every pull request
cubic reviews every pull request against the agents you turn on and comments on the lines that break them.