fix(routing): let fallback skip a candidate the request cannot fit - #1850
Conversation
An oversized request was never silently sent -- `checkInputAdmission` already refuses it before any upstream I/O. The defect is what happens next: that local 413 was classified `request_too_large`, which `comboFailureDecision` treats as `stop`, so the FIRST candidate whose context window was too small ended the fallback chain. A larger-window candidate sitting behind it was never tried. The local preflight refusal now carries its own code, `input_admission_refused`, and that code is hop-eligible. The distinction is the whole point: - `input_admission_refused` is OUR verdict about THIS candidate, so another candidate may well fit. - upstream `context_length_exceeded`, and a generic 413 with no structured code, still stop -- retrying those elsewhere is guesswork. The code is read from the structured field, the classified error and the body text, because the generic classifier maps 413 to its own code and would otherwise swallow the signal on the text path. Driven red with the branch disabled. Nothing here truncates history or drops images to force a fit; an incompatible candidate is skipped, not squeezed. Refs #1524.
|
✅ Deterministic PR hygiene checks passed. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan includes up to 10 reviews per rolling hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe response layer now emits ChangesInput admission failover
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to This localized change lets fallback skip a candidate rejected by local input admission while preserving stop behavior for upstream context errors; the reported tests and typecheck are clean, and no actionable merge-blocking risk remains beyond normal checks. Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d18781cca9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return formatErrorResponse( | ||
| 413, | ||
| "request_too_large", | ||
| "input_admission_refused", |
There was a problem hiding this comment.
Preserve the admission code in the emitted error
When this preflight rejects an oversized non-compaction request, formatErrorResponse passes the message through classifyError; because the message contains “context window,” the formatter emits code: "context_length_exceeded" rather than input_admission_refused. The combo and policy fallback layers therefore receive the existing stop code and never try the larger candidate. Preserve the explicit local-admission code in the serialized response and add focused coverage using the actual formatter or handler output rather than a hand-written error body.
AGENTS.md reference: src/AGENTS.md:L24-L26
Useful? React with 👍 / 👎.
| if (options?.code === "input_admission_refused" | ||
| || error.code === "input_admission_refused" | ||
| || message.includes("input_admission_refused")) { |
There was a problem hiding this comment.
Honor the structured admission code before message inference
Even if the response is changed to preserve input_admission_refused, the real admission message still contains “context window,” so classifyError infers context_length_exceeded and the earlier stop branch returns before this new structured-code check runs. Consequently, comboFailureDecision(413, <real admission body>, { code: "input_admission_refused" }) still returns stop; check the authoritative structured code before the inferred context error and cover the real admission message in the regression test.
AGENTS.md reference: src/AGENTS.md:L24-L26
Useful? React with 👍 / 👎.
Summary
An oversized request was never silently sent —
checkInputAdmission(src/server/responses/input-admission.ts:159) already refuses it before any upstream I/O, and it runs on every concrete retry. The defect #1524 describes is what happens next: that local 413 was classifiedrequest_too_large, whichcomboFailureDecisiontreats asstop, so the first candidate whose context window was too small ended the fallback chain. A larger-window candidate sitting behind it was never tried.The local preflight refusal now carries its own code,
input_admission_refused, and that code is hop-eligible. The distinction is the point:input_admission_refusedis our verdict about this candidate, so another candidate may well fit.context_length_exceeded, and a generic 413 with no structured code, still stop — retrying those elsewhere is guesswork.The code is read from the structured field, the classified error, and the body text, because the generic classifier maps 413 to its own code and would otherwise swallow the signal on the text path.
Nothing here truncates history or drops images to force a fit; an incompatible candidate is skipped, not squeezed.
Scope note. This is the half of #1524 that is provably wrong today and safe to fix now. The broader "re-evaluate every fallback candidate against fresh request evidence" work needs a model-independent context estimate threaded into
PolicyRequestEvidence(which currently leaves request size unknown) and has to reconcile evaluator exact-fit semantics withADMISSION_TOLERANCE = 2.5; that design is written up indevlog/_plan/260816_wave34_closeout/090_1524_capability_preflight.mdand is not in this PR.Refs #1524.
Verification
bun test tests/combos.test.ts— 37 pass / 0 fail, including a new case pinning thatinput_admission_refusedhops while upstreamcontext_length_exceededand a bare 413 still stop. Driven red with the branch disabled.bun test tests/policy-execution.test.ts tests/routing-profile.test.ts— green.bun x tsc --noEmit— clean.Checklist
devSummary by CodeRabbit
Bug Fixes
Tests