fix: resolve all 301 error handling anti-patterns across codebase

Systematic cleanup of every error handling anti-pattern detected by the
automated scanner. 289 issues fixed via code changes, 12 approved with
specific technical justifications.

Changes across 90 files:
- GENERIC_CATCH (141): Added instanceof Error type discrimination
- LARGE_TRY_BLOCK (82): Extracted helper methods to narrow try scope to ≤10 lines
- NO_LOGGING_IN_CATCH (65): Added logger/console calls for error visibility
- CATCH_AND_CONTINUE_CRITICAL_PATH (10): Added throw/return or approved overrides
- ERROR_STRING_MATCHING (2): Approved with rationale (no typed error classes)
- ERROR_MESSAGE_GUESSING (1): Replaced chained .includes() with documented pattern array
- PROMISE_CATCH_NO_LOGGING (1): Added logging to .catch() handler

Also fixes a detector bug where nested try/catch inside a catch block
corrupted brace-depth tracking, causing false positives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-19 19:57:00 -07:00
parent c9adb1c77b
commit a0dd516cd5
91 changed files with 4846 additions and 3414 deletions
+51 -42
View File
@@ -7,6 +7,8 @@
// to parse after each chunk. Once we have valid JSON, we resolve immediately
// without waiting for EOF. This is the proper fix, not a timeout workaround.
import { logger } from '../utils/logger.js';
/**
* Check if stdin is available and readable.
*
@@ -29,9 +31,10 @@ function isStdinAvailable(): boolean {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
stdin.readable;
return true;
} catch {
} catch (error) {
// Bun crashed trying to access stdin (EINVAL from fstat)
// This is expected when Claude Code doesn't provide valid stdin
logger.debug('HOOK', 'stdin not available (expected for some runtimes)', { error: error instanceof Error ? error.message : String(error) });
return false;
}
}
@@ -49,8 +52,9 @@ function tryParseJson(input: string): { success: true; value: unknown } | { succ
try {
const value = JSON.parse(trimmed);
return { success: true, value };
} catch {
// JSON is incomplete or invalid
} catch (error) {
// JSON is incomplete or invalid — expected during incremental parsing
logger.debug('HOOK', 'JSON parse attempt incomplete', { error: error instanceof Error ? error.message : String(error) });
return { success: false };
}
}
@@ -128,47 +132,52 @@ export async function readJsonFromStdin(): Promise<unknown> {
}
}, SAFETY_TIMEOUT_MS);
const onData = (chunk: Buffer | string) => {
input += chunk;
// Clear any pending parse delay
if (parseDelayId) {
clearTimeout(parseDelayId);
parseDelayId = null;
}
// Try to parse immediately - if JSON is complete, resolve now
if (tryResolveWithJson()) {
return;
}
// If immediate parse failed, set a short delay and try again
// This handles multi-chunk delivery where the last chunk completes the JSON
parseDelayId = setTimeout(() => {
tryResolveWithJson();
}, PARSE_DELAY_MS);
};
const onEnd = () => {
// stdin closed - parse whatever we have
if (!resolved) {
if (!tryResolveWithJson()) {
// Empty or invalid - resolve with undefined
resolveWith(input.trim() ? undefined : undefined);
}
}
};
const onError = () => {
if (!resolved) {
// Don't reject on stdin errors - just return undefined
// This is more graceful for hook execution
resolveWith(undefined);
}
};
try {
process.stdin.on('data', (chunk) => {
input += chunk;
// Clear any pending parse delay
if (parseDelayId) {
clearTimeout(parseDelayId);
parseDelayId = null;
}
// Try to parse immediately - if JSON is complete, resolve now
if (tryResolveWithJson()) {
return;
}
// If immediate parse failed, set a short delay and try again
// This handles multi-chunk delivery where the last chunk completes the JSON
parseDelayId = setTimeout(() => {
tryResolveWithJson();
}, PARSE_DELAY_MS);
});
process.stdin.on('end', () => {
// stdin closed - parse whatever we have
if (!resolved) {
if (!tryResolveWithJson()) {
// Empty or invalid - resolve with undefined
resolveWith(input.trim() ? undefined : undefined);
}
}
});
process.stdin.on('error', () => {
if (!resolved) {
// Don't reject on stdin errors - just return undefined
// This is more graceful for hook execution
resolveWith(undefined);
}
});
} catch {
process.stdin.on('data', onData);
process.stdin.on('end', onEnd);
process.stdin.on('error', onError);
} catch (error) {
// If attaching listeners fails (Bun stdin issue), resolve with undefined
logger.debug('HOOK', 'Failed to attach stdin listeners', { error: error instanceof Error ? error.message : String(error) });
resolved = true;
clearTimeout(safetyTimeoutId);
cleanup();