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:
+51
-42
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user