fix(chat-path): classify signal exits before auth fallback (#906)#909
Merged
oleksandr-korin merged 1 commit intoMay 21, 2026
Merged
Conversation
The chat path (claude_code.py) was missing the _classify_signal_exit call that was added to the headless path (headless_executor.py) for Issue #516. When a SIGKILL terminates the claude subprocess at 0 turns (cgroup OOM, host SIGKILL, watchdog cancel), the chat handler would fall straight into _diagnose_exit_failure, which returns "Subscription token may be expired or revoked. Generate a new one with 'claude setup-token'." even when no auth signal was observed. This misclassification: - Misleads operators into chasing token regeneration when the actual cause is OOM / timeout / external kill - Pollutes the SUB-003 auto-switch trigger pattern matcher (which reads the error string), causing spurious subscription rotations on agents whose subscriptions are provably healthy - Burns the auto-switch 2-hour skip-list slot on phantom auth failures Fix mirrors the existing pattern in headless_executor.py:683 — call _classify_signal_exit first, fall through to _diagnose_exit_failure only for non-signal exits. No new logic; the classifier already produces the honest "Execution terminated by SIGKILL after N tool calls / N turns" message. Adds a structural regression test (parametrized over both files) that pins the call ordering — _classify_signal_exit must appear before _diagnose_exit_failure in both call sites, otherwise the auth-fallback heuristic re-introduces the misclassification. Deployment: requires base image rebuild + agent restart for the fix to take effect on running agents (per CLAUDE.md note #7). Out of scope: Fix 2 (gate auto-switch on observed wire 401/403/429) and Fix 3 (cgroup OOM event reading) — both flagged in #906 as follow-up improvements. Fixes #906 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_classify_signal_exitcall to the chat path (claude_code.py:450), mirroring the existing pattern inheadless_executor.py:683that was added for Issue bug: SIGKILL/timeout terminations of claude subprocess misclassified as authentication failure #516Background
Issue #516 introduced
_classify_signal_exitto fix the SIGKILL → "token expired" misclassification in the headless path (/api/task, scheduled tasks). The fix never landed on the parallel chat path (/api/chat, interactive chats), so the same misclassification persists there.Two-codepath analysis from the issue's diagnosis:
_classify_signal_exitfirst?/api/task)headless_executor.py:683/api/chat)claude_code.py:453The misclassification has two compounding effects beyond bad operator-facing text:
Changes
docker/base-image/agent_server/services/claude_code.py(+12)_classify_signal_exitto the imports fromerror_classifier_diagnose_exit_failurein the chat handler'sif return_code != 0block. Pattern matchesheadless_executor.py:683-687exactly.tests/unit/test_signal_exit_classification.py(+52)claude_code.py,headless_executor.py)_classify_signal_exit(appears before_diagnose_exit_failure(in each file's source. Order reversal in either file would re-introduce the misclassification — verified by manually flipping the order and confirming the test fails.Out of scope (deferred to follow-ups)
error_classfield to executions and refactoring the SUB-003 matcher to read it instead of the freeform error text. Larger refactor; filed for follow-up.memory.events.oom_killincremented. Optional per the issue body.Deployment
This change touches
docker/base-image/agent_server/. PerCLAUDE.mdnote #7, base image rebuild + agent restart is required for the fix to take effect on running agents:./scripts/deploy/build-base-image.sh # Then restart agents (e.g. via /agent stop+start in UI, or fleet-wide restart)Until the rebuild + restart, agents already running will continue to emit the old misclassified error.
Test plan
python3.11 -m pytest tests/unit/test_signal_exit_classification.py --confcutdir=tests/unit -q— 23 passed (21 existing + 2 new parametrized cases)claude_code.py— test fails as expected, then restoresFixes #906
🤖 Generated with Claude Code