Claude CLI under CLAUDE_CODE_OAUTH_TOKEN exits 1 without setting `is_error` when the OAuth subscription's quota is exhausted. The existing fallback chain (`lastResultError || stderr || tailLines(stdout)`) had nothing structured to grab and dumped ~2KB of `system/init` NDJSON into the progress comment, hiding the actionable quota notice the CLI had already printed as plain text. Capture non-JSON stdout lines into a 20-line ring buffer (mirroring the existing `recentStderr` pattern) and prefer it over the raw NDJSON tail. Generic — no regex on bubble text — so any human-readable line the CLI emits surfaces instead of the event stream. Also adds a `failure:claude-oauth-quota` bucket to `analyze-logs.ts`, ordered before the SIGTERM check so the NDJSON tail's `cancelled` / `cancel_url` substrings (from learnings content) stop shadowing it.
This commit is contained in:
committed by
pullfrog[bot]
parent
d857e06731
commit
56793d4a81
@@ -549,6 +549,13 @@ export async function runClaude(params: RunParams): Promise<ClaudeRunResult> {
|
||||
};
|
||||
|
||||
const recentStderr: string[] = [];
|
||||
// ring buffer of recent non-JSON stdout lines. Claude CLI prints
|
||||
// human-readable TTY chrome (status bubbles, quota notices, etc.)
|
||||
// alongside the NDJSON event stream. when the CLI exits non-zero without
|
||||
// emitting a structured error event, these lines are the only actionable
|
||||
// signal — preferring them over the NDJSON tail keeps progress comments
|
||||
// readable. issue #643.
|
||||
const recentNonJsonStdout: string[] = [];
|
||||
|
||||
let lastProviderError: string | null = null;
|
||||
|
||||
@@ -594,6 +601,8 @@ export async function runClaude(params: RunParams): Promise<ClaudeRunResult> {
|
||||
event = JSON.parse(trimmed) as ClaudeEvent;
|
||||
} catch {
|
||||
log.debug(`» non-JSON stdout line: ${trimmed.substring(0, 200)}`);
|
||||
recentNonJsonStdout.push(trimmed);
|
||||
if (recentNonJsonStdout.length > MAX_STDERR_LINES) recentNonJsonStdout.shift();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -695,9 +704,16 @@ export async function runClaude(params: RunParams): Promise<ClaudeRunResult> {
|
||||
const stdoutSnapshot = output.toString();
|
||||
const stderrSnapshot = recentStderr.join("\n");
|
||||
const truncatedStdout = stdoutSnapshot ? tailLines(stdoutSnapshot, 2048) : "";
|
||||
// prefer non-JSON stdout (human-readable TTY chrome the CLI prints,
|
||||
// including status bubbles and quota notices) over the raw NDJSON
|
||||
// tail. when the CLI exits 1 without emitting `is_error` (issue #643),
|
||||
// the NDJSON fallback would otherwise dump 2KB of `system/init` events
|
||||
// into the progress comment with no mention of the actual cause.
|
||||
const nonJsonStdoutSnapshot = recentNonJsonStdout.join("\n");
|
||||
const errorMessage =
|
||||
lastResultError ||
|
||||
stderrSnapshot ||
|
||||
nonJsonStdoutSnapshot ||
|
||||
truncatedStdout ||
|
||||
`unknown error - no output from Claude CLI${errorContext}`;
|
||||
log.error(
|
||||
|
||||
Reference in New Issue
Block a user