No catch-all try/catch blocks that swallow errors
Why it matters
A try/catch block in a test that has no assertion, no throw, and no console.error converts every runtime error into a silent pass. When the production function throws — due to a network failure, a missing DB record, an uncaught null — the test catches it, discards it, and reports success. This pattern is common in AI-generated tests that wrap async calls defensively without understanding that swallowing errors in tests defeats the entire purpose of having tests. ISO-25010:2011 fault-tolerance requires that faults be detectable, not hidden.
Severity rationale
High because silent error-swallowing means production failures that would be caught by tests instead generate a green CI run.
Remediation
Replace bare catch {} or catch { /* ignore */ } blocks in tests with either an expect(...).rejects.toThrow() assertion or a re-throw. Never discard an error inside a test:
// Before: fetchUser throwing is treated as success
it('fetches user', async () => {
try {
await fetchUser('123')
} catch {
// ignore
}
})
// After: error path is explicitly asserted
it('throws for unknown user', async () => {
await expect(fetchUser('does-not-exist')).rejects.toThrow('User not found')
})
// Or: surface real failures immediately
it('fetches user', async () => {
const user = await fetchUser('123')
expect(user.id).toBe('123')
})
Detection
-
ID:
no-error-swallowing-try-catch -
Severity:
high -
What to look for: Walk all test files. Count all
try {blocks within anit(/test(/describe(callback where the correspondingcatchblock contains: NO assertion call (expect(/assert(), AND NOthrow, AND NOconsole.error. Such catch blocks silently swallow errors and let the test "pass" even when the system under test throws. -
Pass criteria: 0 error-swallowing catch blocks in test files. Report: "X test files inspected, 0 error-swallowing catches found."
-
Fail criteria: At least 1 try/catch in a test where the catch block has no assertion, no throw, and no error log.
-
Do NOT pass when: A catch block ends with
// ignoreor/* swallow */— those are still swallowing errors. -
Skip (N/A) when: Project has 0 test files.
-
Detail on fail:
"1 error-swallowing catch: tests/api.test.ts line 22 wraps fetchUser() in try { ... } catch { /* ignore */ } — if fetchUser throws, the test passes silently" -
Remediation: A catch block with no assertion converts every test failure into a test pass. Either assert the error OR re-throw it:
// Bad: silently swallows errors it('fetches user', async () => { try { await fetchUser('123') } catch { // ignore — but the test passes when fetchUser is broken } }) // Good: assert the expected error it('throws on invalid user', async () => { await expect(fetchUser('invalid')).rejects.toThrow('User not found') }) // Or: re-throw to surface real failures it('fetches user', async () => { try { const user = await fetchUser('123') expect(user.id).toBe('123') } catch (err) { throw err // surface the actual failure } })
External references
- iso-25010:2011 · reliability.fault-tolerance — Fault tolerance (reliability sub-characteristic)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-test-theater·automated