Repository navigation
fix(combo): log the upstream refusal reason on combo target failure - #7320
vadymhimself wants to merge 2 commits into
Conversation
The combo hop warning printed only `<target> failed with 401 after Nms`, so a revoked credential or plan downgrade logged hundreds of identical lines that read like a provider outage. Append the upstream error code and parsed message (falling back to the normalized body text), collapsed to one line, redacted and capped at 200 characters via sanitizeLogMetadataString. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
✅ Deterministic PR hygiene checks passed. |
⏳ DRAFT
What to do
Review readiness checklist
0/4 boxes ticked. This PR stays in draft until every box above is ticked. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
Intake/scoped review at The warning adds upstream refusal metadata through the existing sanitizer and one-line bound. Because classificationText can originate in an upstream envelope, the log/credential review must establish the allowed fields rather than treating string truncation as a privacy proof. This intake does not certify that new logging boundary or initiate a scan. Current-head functional workflows are awaiting fork approval; that is pending execution, not a passing test result. No local suite was run for this branch in this intake. No workflow approval, new scan, live configuration change or merge was performed. Required review/readiness gates remain in force. |
…ilure Drop the classificationText fallback so an unparsed upstream body never reaches the warning line; the log boundary is now the two parsed fields. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Thanks for the intake. On the logging boundary: the warning now carries only two parsed fields, 🤖 Generated with Claude Code |
|
Thank you @vadymhimself! Both commits are absorbed into #7355 with your authorship intact. One follow-up commit there redacts the parsed fields before collapsing whitespace, so multiline credential framing in an upstream |
) (#7355) * fix(combo): retry zero-output response.incomplete with no reason A combo target that ends with `response.incomplete` before any output and without `incomplete_details.reason` was accepted as the turn's answer, so the caller got an empty incomplete turn instead of failover to the next target. Treat a missing reason like the transport reasons already listed; semantic reasons such as max_output_tokens still commit the target. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(combos): hold the turn lease across the failover ladder Each combo child relay binds the turn admission lease to its own controller and unbinds it when its stream ends; the lease releases once nothing is bound. When target 1 returned 200 SSE and then failed before output, the combo cancelled that relay, its unbind released the parent's lease, and target 2 was aborted on bind with "turn already settled" (502). handleComboResponses now binds its own controller for the whole ladder and unbinds it on return. The accepted child's stream is bound before that, so it keeps the lease until it ends. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(responses): count Combo recovery sends in the shared send budget * fix(responses): preserve reserved Anthropic pool recovery sends * test(adapters): assert exact combo cooldown rejection and single refund * fix(combo): log the upstream refusal reason on combo target failure The combo hop warning printed only `<target> failed with 401 after Nms`, so a revoked credential or plan downgrade logged hundreds of identical lines that read like a provider outage. Append the upstream error code and parsed message (falling back to the normalized body text), collapsed to one line, redacted and capped at 200 characters via sanitizeLogMetadataString. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(combo): log only the parsed upstream code and message on a hop failure Drop the classificationText fallback so an unparsed upstream body never reaches the warning line; the log boundary is now the two parsed fields. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(combo): redact failure fields before flattening log metadata Preserve credential framing until the shared redactor has removed credential values. Cover multipart framing in a parsed upstream code at the actual combo hop warning boundary. Co-authored-by: Vadym O <bolein95@gmail.com> * fix(combo): retain output carried by reasonless incomplete snapshots A terminal-only upstream can include text or tool items in its final snapshot. Keep that response committed, and cover explicit semantic/unknown reasons and output-first streams as well. Co-authored-by: Vadym O <bolein95@gmail.com> * fix(combo): unbind only registered turn lease controllers Match registerTurn capability checks for plain admission leases so the ladder does not report a spurious release miss. Add accepted fallback stream completion, cancellation and caller-abort coverage with exactly-once attached lease settlement. Co-authored-by: Vadym O <bolein95@gmail.com> * docs(combo): describe incomplete, lease and recovery-budget boundaries * docs(structure): split combo streaming boundaries within the line budget --------- Co-authored-by: Vadym O <bolein95@gmail.com> Co-authored-by: Claude Code <noreply@anthropic.com> Co-authored-by: Epinephrine <luvs01@hanmail.net>
Summary
executeComboResponseslogged only[combo] <id>: <target> failed with <status> after <n>ms. A revoked ChatGPT session, an invalid key, or a plan downgrade therefore produced long runs of identicalfailed with 401lines with nothing saying why, which read like a provider outage.upstreamCode,upstreamMessage, falling back to the normalizedclassificationText). No extra body read. The reason is collapsed to one line, redacted, stripped of control characters and capped at 200 characters by the existingsanitizeLogMetadataStringhelper, so a failure stays one log line and credentials echoed by an upstream body are not printed.[combo] fan: primary/model-primary failed with 401 after 2ms: invalid_api_key: Incorrect API key provided: <redacted>. Rotate it.src/server/responses/core-combo.ts; routing, cooldown and the client response are unchanged.Verification
the hop warning names the upstream refusal on one redacted lineintests/responses/responses-combo-exhausted-error.test.ts, run before the src change:bun test tests/responses/responses-combo-exhausted-error.test.ts-> 16 pass, 1 fail (Received: "[combo] fan: primary/model-primary failed with 401 after 2ms"). After the change: 17 pass, 0 fail. The test also asserts that the API key from the upstream body is not in the line.bun x tsc --noEmit-> exit 0.bun run test:changed(Bun 1.4.3 viaOCX_TEST_RUNNER_BUN) -> 15894 pass, 34 skip, 1 fail across 636 files. The single failure,tests/cli/cli-connect-readiness.test.ts"a rejected preferred runtime falls back without rewriting the saved selection", fails identically on an unmodifiedupstream/dev8640c2f checkout on this machine (local-environment dependent) and is unrelated to combo logging.bun run testwas not run locally:test:changedalready covered 636 files through the changed module's import graph; the remainder is left to CI.Checklist
sanitizeLogMetadataStringbefore it reaches the log; covered by the test.)Source: carried from the vadymhimself fork build (commits c234b70 and fde8786,
core-combo.tshunks), re-authored to reusesanitizeLogMetadataString.🤖 Generated with Claude Code
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
Required local validation passed; commands, results, and any full-suite exception are documented.
I pushed my PR to a recent dev commit (at most 10 behind; a maintainer may still ask for the exact tip before merge).
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.
Summary by CodeRabbit