Repository navigation
Agent pane: a turn whose model can't be reached says so, not Thinking (acpmux turn_retrying) - #34306
Conversation
…ot Thinking
Leo, 2026-11-03, on "Thinking 7m 49s" from a chat whose model gateway was
down: "i feel like this mac session just keeps "thinking" but idk if tool
calls are somehow being hidden or if that workspace itself/session itself
under acpmux is kinda weird".
acpmux logs each harness retry of a failed model request as turn_retrying
{turnId, attempt, maxRetries, error, errorStatus, retryDelayMs, source}:
Claude Code's system/api_retry lines and Codex's willRetry stream errors
(hub/turn_retry.rs), in the transcript category. The model's next sign logs
turn_heard. The agent pane's live line then reads "Can't reach the model ·
retrying (7 of 10) · 503 server error" (or "The model is busy" for 429/529),
calm, with no sweep and no clock, in the same box; once the model answers,
the retries' time leaves the Thinking clock. A retry that runs out ends the
turn failed on the existing error card with Retry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-fb75b7af9902231028aa
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-fb75b7af9902231028aa
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on f382257 to compare; do not merge until green; see lane-rules Shipped in:
|
|
Owned by an agent session · session |
|
Review (codex, router-review): SHIP at a51f918 Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 3. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward. SHIP at a51f918
Cargo and Bun were unavailable; source review and the extracted error-label reproduction completed. Follow-ups:
|
Router review follow-up on #34306: the busy regex's word boundary missed codes with a trailing _error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-fb75b7af9902231028aa
teamleaderleo
left a comment
There was a problem hiding this comment.
Review (codex gpt-6-astra high, next-review, since=20261103T232347Z): SHIP at d32ae48
SHIP at d32ae48
Visual verdict: no images; Leo tries the build
-
[P2] [follow-up] turn_retry.rs:103: Background subagent progress clears the retry warning prematurely. The reused
silent_turnstate is consumed byheard_after_silenceforsubagent_progress, child stream events, and tool updates—not necessarily recovery of the failed model request. Keep retry recovery separate from general activity detection and test a main request retrying while a background subagent emits progress. -
[P2] [follow-up] direct.ts:6218: The warning can remain after successful recovery across a daemon restart.
silent_turninitializes empty and host adoption does not restore it, so the next successful response emits noturn_heard. The client replaysturn_retryingand keeps displaying it until turn end. Restore the pending retry during adoption and cover restart between retry and recovery. -
[P2] [follow-up] turns.ts:1155: Retry display is skipped when
agent.sessionStatusis enabled and a todo list produces anACTIVITY_LINE; layouts producingWORKINGalso bypass this guard. Those lines continue showing ordinary activity and elapsed time during an outage. Handle these supported live-line variants and add coverage. -
[P2] [follow-up] TurnRetry.gallery.ts:19: Supply rendered gallery images, including a narrow pane, to verify truncation and layout stability.
Bun and Cargo were unavailable, so tests could not be rerun.
Follow-ups:
- Scope retry recovery to the affected model request.
- Restore pending retries during daemon adoption.
- Render retry status across live-line variants.
- Capture gallery images.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-fb75b7af9902231028aa
|
Found 43 test failures on Blacksmith runners: Failures
...and 33 more test failures. View all on Blacksmith
|
…t, not retry["turnId"] = 8f918fa (#34306) writes retry["turnId"] with IndexMut on a serde_json Value, which clippy::indexing_slicing counts. cmux_json::JsonSet::set writes the same key on an object (and on null, as IndexMut does) and never panics; a non-object retry is left as it is. Sites from the panic step's clippy JSON: last green tip ab54fff (fleet step 294210d667444f201633a143) against feat 851f9ed plus this branch's fixes, with the acpmux and cmux-tui rows raised on a throwaway branch for the diagnosis only (fleet step 20552d31e2aa6f074ebbba6e).
…uggestion gate, and link sync index reads (cmux-automations 1, cmux-tui, acpmux) (#34735) * base red: crash ratchet: notification.clear reads notification_id with a typed error, not expect 7467cdd (#34498) added `value.as_str().expect("catalog resource-id validation")` for the new notification_id field of notification.clear, which took the cmux-tui-core expect row from 190 to 191 and turned the crash ratchet red on feat-cmux-next. A non-string notification_id now returns the same validation error that notification_workspace returns for workspace_id (#23530 did the same for that field). resource_router.rs: 7 expects at 7467cdd^, 8 at 7467cdd, 7 here. `python3 scripts/cmux-next/crash_ratchet.py`: red at 6767426 (expect 190 -> 191), ok here. The catalog validates the field as a resource id first, so no request reaches the old expect; no behavioral test can fail on the base, and the ratchet is the regression check. * base red: panic ratchet: cmux-automations import reads the ledger id with get, not v["automation"] The panic ratchet (scripts/cmux-next/rust_panic_ratchet.py) on feat-cmux-next reports "cmux-automations: clippy::indexing_slicing 1 > new crate (must be 0)". cmux-automations is a new crate in the ratchet, so it must have 0 sites. The site is Store::import in store.rs. It reads the import ledger entry with v["automation"] on a serde_json::Value. Commit dbb578b ("teleport between this Mac, cmux Cloud and ssh:HOST; MCP automation tools", pushed directly, with no PR) added it. The fix uses v.get("automation").and_then(Value::as_str). Behavior does not change: the Index on a Value gives Null when the key is missing or the value is not an object, and as_str on Null gives None. get gives None in the same cases. Thus the id is "" in the same cases as before. The baseline is not raised. No allow and no crash-allow is added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * base red: panic ratchet: cmux automation's Cloud edit and audit set params with JsonSet, not params["..."] = dbb578b (teleport between this Mac, cmux Cloud and ssh:HOST) added three IndexMut writes on a serde_json Value in cli/automation.rs (the Cloud `edit` name and paused fields, and the `audit` automation filter). clippy::indexing_slicing counts each one, which took cmux-tui from 831 to 834 and kept the panic ratchet red on feat-cmux-next. cmux_json::JsonSet sets the same keys on the same objects and never panics. Sites from the panic step's clippy JSON: the last green tip ab54fff (fleet step 294210d667444f201633a143) against this branch's parent with the cmux-tui row raised for the diagnosis only (fleet step 33bab4d69e741549bf0dac14): +3 in cmux-tui, all at cli/automation.rs 1338, 1341 and 1380. Behavior is the same: each params value is a JSON object built by json!({...}) just before. * base red: panic ratchet: acpmux task plan updates its task with get_mut, not self.tasks[at] f61b2d9 (#34567) indexes self.tasks[at] in TaskPlan's update, and clippy::indexing_slicing counts it (acpmux 689 > 686 with the next two commits). at comes from position() just before, so get_mut(at)? finds the same task and never returns None there. Sites from the panic step's clippy JSON: last green tip ab54fff (fleet step 294210d667444f201633a143) against feat 851f9ed plus this branch's fixes, with the acpmux and cmux-tui rows raised on a throwaway branch for the diagnosis only (fleet step 20552d31e2aa6f074ebbba6e). * base red: panic ratchet: acpmux turn_retrying sets turnId with JsonSet, not retry["turnId"] = 8f918fa (#34306) writes retry["turnId"] with IndexMut on a serde_json Value, which clippy::indexing_slicing counts. cmux_json::JsonSet::set writes the same key on an object (and on null, as IndexMut does) and never panics; a non-object retry is left as it is. Sites from the panic step's clippy JSON: last green tip ab54fff (fleet step 294210d667444f201633a143) against feat 851f9ed plus this branch's fixes, with the acpmux and cmux-tui rows raised on a throwaway branch for the diagnosis only (fleet step 20552d31e2aa6f074ebbba6e). * base red: panic ratchet: acpmux suggestion gate marks a withheld suggestion with JsonSet, not nested IndexMut a4f11cd (#34496) writes _meta.acpmux.suggestionWithheld through nested IndexMut, which clippy::indexing_slicing counts (3 new hits). The same writes now go through cmux_json::JsonSet and get_mut: a non-object _meta or _meta.acpmux still becomes {} first, and an update that is not an object (where IndexMut would panic) is left as it is. Sites from the panic step's clippy JSON: last green tip ab54fff (fleet step 294210d667444f201633a143) against feat 851f9ed plus this branch's fixes, with the acpmux and cmux-tui rows raised on a throwaway branch for the diagnosis only (fleet step 20552d31e2aa6f074ebbba6e). * base red: panic ratchet: cmux link sync's relative link and readlink answer read without an index 6b9bafd (#34362) added &to[common..] in relative_from and answer["target"] in readlink; clippy::indexing_slicing counts both (cmux-tui +2 over 831 after the teleport fix in this branch). common is at most to.len(), so to.get(common..) gives the same slice; answer.get("target") gives None where answer["target"] gave Null, and both end in the same "fs.readlink answered no target" error. Sites from the panic step's clippy JSON: last green tip ab54fff (fleet step 294210d667444f201633a143) against feat 851f9ed plus this branch's fixes, with the acpmux and cmux-tui rows raised on a throwaway branch for the diagnosis only (fleet step 20552d31e2aa6f074ebbba6e). --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Leo, 2026-11-03, on a chat that read "Thinking 7m 49s": "i feel like this mac session just keeps "thinking" but idk if tool calls are somehow being hidden or if that workspace itself/session itself under acpmux is kinda weird".
The chat was not thinking. Its model gateway was down, and Claude Code was retrying the request (
system/api_retry, attempt 1..10,error_status,error, about 75 s apart). acpmux logged those lines but nothing the pane reads said so, so the live line kept reading "Thinking" with a clock that counted the retries.hub/turn_retry.rs): each harness retry of a failed model request logs a mux recordturn_retrying{turnId, attempt, maxRetries, error, errorStatus, retryDelayMs, source}. Sources: Claude Code'ssystem/api_retryline and Codex'ssession_info_update._meta.codex.errorwithwillRetry("Reconnecting... 2/5", HTTP status fromcodexErrorInfo). It is in thetranscriptcategory, so clients get it with no new subscription. The turn is marked silent, so the model's next sign logsturn_heard, which ends the retry. Documented in the README anddocs/acpmux-schema.json.direct.tskeeps the retry on a client-onlyTURN_RETRYrow (kindtyping, drawn by nothing).turns.tswithRetryputs it on the live line, andThinking.tsxthen reads "Can't reach the model · retrying (7 of 10) · 503 server error", or "The model is busy" for 429/529 and rate-limit or overload errors. It is calm: no shimmer, no clock, the cause in text ink and the count and error muted, with the error the part that truncates. It uses the same.cv-workedbox, so nothing moves when it starts or ends. Once the model answers, the retries' time comes off the "Thinking" clock (before the first output, the clock times the turn from its prompt).claude.system.api_retry("Retrying (2 of 10, error 503)"), so this adds no parity gap.Gallery:
agent-pane.turn-retry, with variantsretrying,retrying-with-error,retrying-busyandgiven-up. The mock daemon has the live scene at?mock&session=mock-model-retry(seventurn_retryingrecords).Testing
cmux-ci test(Linux):cargo clippy -p acpmux --all-targets -D warningsis clean.cargo test -p acpmux --lib turn_retry(3 unit tests) passes.--test claude_stream_timingpasses; its newa_claude_api_retry_logs_turn_retrying_until_the_model_answersuses fake Claude'sapi-retry-probe.--test hub_integration -- retry silentpasses 44 tests, including the Codex retry test, which now assertsturn_retryingcomes beforeturn_heard, which comes before the redelivered answer.cargo fmtwas fixed in the second commit.turnRetry.live.test.tsfeeds real records throughAcpmuxDirectClientover the mock socket and checks the retry row, the retry time, and the cleanup at turn end. It also coverswithRetryandretryParts.test/transcript-turn-retry.test.tsxrenders the gallery snapshots throughturnViewandThinking.Observed / Not run
Changelog
Fixed: A chat whose model can't be reached now says "Can't reach the model · retrying (N of M)" instead of "Thinking".
Proof
No capture: webview UI. See gallery
agent-pane.turn-retry.Agent session: agent session
🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Shows when a chat's model can't be reached instead of reading "Thinking" while the harness retries the request.
Previously a turn whose model gateway was down kept its "Thinking" live line with a clock counting the retries. Now acpmux logs each retry, and the agent pane's live line reads "Can't reach the model · retrying (7 of 10) · 503 server error" (or "The model is busy" for 429/529 and rate-limit or overload errors, including status-less codes like
rate_limit_error), calmly, with no sweep and no clock. When the model answers, the line returns to "Thinking" minus the time the retries took. Once a retry runs out, the turn still ends failed on the existing error card with Retry.acpmux
turn_retryingmux record (transcript category), sourced from Claude Code'ssystem/api_retryand Codex's_meta.codex.errorwithwillRetry.turn_heard, ending the retry.Agent pane
TURN_RETRYrow, drawn in the same.cv-workedbox so nothing moves when retries start or end.Written for commit 0e5fc23. Summary will update on new commits.