Summary
Two related bugs observed in the detection job when Claude Code CLI fails due to a rate-limit (429) error:
- The 429 is not classified as a rate-limit error — the harness logs
isRateLimitError=false even though the Claude Code result JSON clearly contains a 429 error. Rate-limit-specific retry backoff is therefore not applied.
- The
detection job silently passes on Claude Code CLI exit code 1 — when all retries are exhausted and Claude Code exits with code 1, the Parse and conclude threat detection step maps the outcome to conclusion: warning rather than failure, making the whole job conclude success. This is a security gap: if the threat detection agent fails entirely, the workflow should be blocked, not silently approved.
Reproduction
The following sequence was observed in a detection job run:
Step: Execute Claude Code CLI
Claude Code CLI made a few tool calls, then hit a rate limit:
{
"type": "result",
"subtype": "success",
"is_error": true,
"api_error_status": 429,
"result": "API Error: Request rejected (429) · This request would exceed your account's rate limit. Please try again later.",
"stop_reason": "stop_sequence"
}
The harness retried 4 times, but classified the error as not a rate limit:
[claude-harness] attempt 1 failed: exitCode=1 isOverloadedError=false isRateLimitError=false isMaxTurnsExit=false hasOutput=true retriesRemaining=3
[claude-harness] attempt 2 failed: exitCode=1 isOverloadedError=false isRateLimitError=false isMaxTurnsExit=false hasOutput=true retriesRemaining=1
[claude-harness] all 3 retries exhausted — giving up (exitCode=1)
##[error]Process completed with exit code 1.
Step: Parse and conclude threat detection
Despite the CLI exiting with code 1 and producing no usable output, the parse step emitted a warning (not failure):
##[error]❌ Failed to parse detection result: No THREAT_DETECTION_RESULT found in detection log. The detection model may have failed to follow the output format.
##[warning]⚠️ ERR_PARSE: ❌ No THREAT_DETECTION_RESULT found in detection log.
The step and job both concluded success.
Root Cause Analysis
Bug 1 — 429 misclassified: The Claude Code CLI wraps the 429 inside its result JSON (is_error: true, api_error_status: 429, stop_reason: stop_sequence) rather than propagating it as a transport-level HTTP error. The harness's isRateLimitError detection appears to check for a different signal (e.g. a transport-level retry header or a specific exit code), so it misses the 429 embedded in the JSON body. The result: no rate-limit backoff, immediate exhaustion of retries.
Bug 2 — Silent pass on detection failure: When ERR_PARSE occurs (no THREAT_DETECTION_RESULT in the log), the harness sets conclusion: warning instead of conclusion: failure. This makes the detection job green even when the agent ran zero useful turns. The safe_outputs job then proceeds as if detection succeeded — a meaningful security gap.
Expected Behavior
- When
api_error_status: 429 is present in the Claude Code result JSON, isRateLimitError should be true, and rate-limit retry backoff should be applied.
- When Claude Code exits with code 1 and produces no
THREAT_DETECTION_RESULT, the detection job should fail (not warn), blocking downstream jobs including safe_outputs.
Environment
- Claude Code CLI version: current (as installed by gh-aw harness)
- Model:
claude-sonnet-4-6
Reported by Claude (claude-sonnet-4-6) based on analysis of detection job logs.
Summary
Two related bugs observed in the
detectionjob when Claude Code CLI fails due to a rate-limit (429) error:isRateLimitError=falseeven though the Claude Code result JSON clearly contains a 429 error. Rate-limit-specific retry backoff is therefore not applied.detectionjob silently passes on Claude Code CLI exit code 1 — when all retries are exhausted and Claude Code exits with code 1, theParse and conclude threat detectionstep maps the outcome toconclusion: warningrather thanfailure, making the whole job concludesuccess. This is a security gap: if the threat detection agent fails entirely, the workflow should be blocked, not silently approved.Reproduction
The following sequence was observed in a
detectionjob run:Step: Execute Claude Code CLI
Claude Code CLI made a few tool calls, then hit a rate limit:
{ "type": "result", "subtype": "success", "is_error": true, "api_error_status": 429, "result": "API Error: Request rejected (429) · This request would exceed your account's rate limit. Please try again later.", "stop_reason": "stop_sequence" }The harness retried 4 times, but classified the error as not a rate limit:
Step: Parse and conclude threat detection
Despite the CLI exiting with code 1 and producing no usable output, the parse step emitted a warning (not failure):
The step and job both concluded
success.Root Cause Analysis
Bug 1 — 429 misclassified: The Claude Code CLI wraps the 429 inside its result JSON (
is_error: true,api_error_status: 429,stop_reason: stop_sequence) rather than propagating it as a transport-level HTTP error. The harness'sisRateLimitErrordetection appears to check for a different signal (e.g. a transport-level retry header or a specific exit code), so it misses the 429 embedded in the JSON body. The result: no rate-limit backoff, immediate exhaustion of retries.Bug 2 — Silent pass on detection failure: When
ERR_PARSEoccurs (noTHREAT_DETECTION_RESULTin the log), the harness setsconclusion: warninginstead ofconclusion: failure. This makes thedetectionjob green even when the agent ran zero useful turns. Thesafe_outputsjob then proceeds as if detection succeeded — a meaningful security gap.Expected Behavior
api_error_status: 429is present in the Claude Code result JSON,isRateLimitErrorshould betrue, and rate-limit retry backoff should be applied.THREAT_DETECTION_RESULT, the detection job should fail (not warn), blocking downstream jobs includingsafe_outputs.Environment
claude-sonnet-4-6Reported by Claude (claude-sonnet-4-6) based on analysis of detection job logs.