Skip to content

fix: transition Claude Code session to idle when API retries are exhausted#60

Merged
110y merged 1 commit into
mainfrom
529-abortion
May 26, 2026
Merged

fix: transition Claude Code session to idle when API retries are exhausted#60
110y merged 1 commit into
mainfrom
529-abortion

Conversation

@110y

@110y 110y commented May 26, 2026

Copy link
Copy Markdown
Owner

When the Claude API returns a terminal error (e.g. 529 Overloaded) and retries are exhausted, Claude Code writes a synthetic assistant message with isApiErrorMessage: true to the JSONL but does not fire a Stop hook. As a result, the muxac session status stayed stuck at running even though the agent was no longer processing.

The monitor now inspects the last JSONL line for that synthetic API-error marker, and when found with a timestamp newer than the session's updated_at, transitions running/waiting sessions to idle (using the existing CAS update for safe concurrent writes). Includes tests covering the running/waiting transitions, the recency guard, the last-line guard, and the case where intermediate type:"system",subtype:"api_error" retry logs must not trigger the transition.

Copilot AI review requested due to automatic review settings May 26, 2026 04:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Claude Code session monitor to correctly transition sessions to idle when Claude Code terminates due to an exhausted-retries API failure (where no Stop hook is emitted), by detecting a synthetic JSONL assistant message marker.

Changes:

  • Extend JSONL parsing to recognize Claude Code’s synthetic API error termination message (isApiErrorMessage).
  • Update Claude Code session sync logic to transition running/waitingidle when the last JSONL line indicates terminal API error and is newer than the session’s updated_at.
  • Add test coverage for the new transition behavior, including recency and “last-line only” guards and ignoring non-terminal retry logs.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
internal/monitor/monitor.go Detect synthetic terminal API-error JSONL line and CAS-transition session status to idle.
internal/monitor/monitor_test.go Add tests covering running/waitingidle on terminal API error and guard cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 256 to +306
if len(lines) > 0 {
var lastLine jsonlLine
var maxTimestamp string
for _, raw := range lines {
var line jsonlLine
if err := json.Unmarshal([]byte(raw), &line); err != nil {
continue
}
lastLine = line
if line.Timestamp != "" && (maxTimestamp == "" || isAfter(line.Timestamp, maxTimestamp)) {
maxTimestamp = line.Timestamp
}
}

// Interruption check takes priority over waiting→running.
if isInterruptionLine(lastLine) && isAfter(lastLine.Timestamp, sess.UpdatedAt) {
st := status.Status(sess.Status)
if st == status.Running || st == status.Waiting {
if err := queries.UpdateSessionStatusIfUnchanged(ctx, sqlc.UpdateSessionStatusIfUnchangedParams{
Status: string(status.Idle),
UpdatedAt: timestamp.Now(),
WaitingSince: "",
Name: sess.Name,
Path: sess.Path,
Status_2: string(st),
}); err != nil {
return fmt.Errorf("update status to idle: %w", err)
}
}
return nil
}

// API error termination (e.g. 529 Overloaded after retry exhaustion):
// Claude Code does not fire a Stop hook in this case, so the session
// would otherwise remain stuck in `running`.
if isAPIErrorLine(lastLine) && isAfter(lastLine.Timestamp, sess.UpdatedAt) {
st := status.Status(sess.Status)
if st == status.Running || st == status.Waiting {
if err := queries.UpdateSessionStatusIfUnchanged(ctx, sqlc.UpdateSessionStatusIfUnchangedParams{
Status: string(status.Idle),
UpdatedAt: timestamp.Now(),
WaitingSince: "",
Name: sess.Name,
Path: sess.Path,
Status_2: string(st),
}); err != nil {
return fmt.Errorf("update status to idle on api error: %w", err)
}
}
return nil
}
@110y 110y merged commit cb62435 into main May 26, 2026
5 checks passed
@110y 110y deleted the 529-abortion branch May 26, 2026 05:01
@muxac-cd muxac-cd Bot mentioned this pull request May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants