fix(errors): Codex entitlement/retired-model 400 with a top-level detail body is reported as a model rejection, not context overflow (#81558, #106475, salvage #100783) - #115881
Merged
teknium1 merged 1 commit intoSep 19, 2026
Conversation
… a bare 400
`agent/error_classifier.py::_body_message_candidates` never yielded the
FastAPI-style top-level `detail` key (string, or nested `{"message": ...}`),
so the Codex gateway's `{"detail": "The '<model>' model is not supported when
using Codex with a ChatGPT account."}` 400 read as a *bare* 400. On a large
session `_classify_400`'s generic-400 heuristic then classified it as
context_overflow: the loop burned compression attempts ("Context length
exceeded (109,962 tokens). Cannot compress further.") and, because
`is_client_error` excludes overflow, the entitlement marker from #106549 never
ran. Reading `detail` makes it a descriptive rejection (format_error: abort +
fall back, no compression) and surfaces the provider's text as the message
instead of `Error code: 400 - {...}`. The pydantic list shape of `detail` is
still handled by `_oversized_message_content_rejection` and is not yielded.
Slim re-port of #100783's detail-body half onto the rule-table classifier;
the session/weekly usage-limit half is a separate class and was dropped.
Refs #81558
Refs #106475
Co-authored-by: Oleg Nagornyy <nagornyy.o@gmail.com>
Contributor
|
Independent verification on the PR head (7c40b50): error-classifier suite 169/169 green on Linux. Reading a top-level detail body turns a bare 400 into a descriptive error. No findings. |
Contributor
૮ >ﻌ< ა ci reviewran on 7c40b50 — fix: read a top-level all good! |
teknium1
deleted the
fix/boa-res-R4-routing-catalog-azure-detail-classifier
branch
September 19, 2026 17:23
This was referenced Sep 19, 2026
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.
A Codex entitlement/retired-model
400 {"detail": "The '<model>' model is not supported when using Codex with a ChatGPT account."}on a large session is now reported as a descriptive request rejection with the provider's own text, instead of being mistaken for context overflow and burning the compression loop.agent/error_classifier.py::_body_message_candidatesnow yields a top-leveldetail(string, or nested{"message": ...}) after the existing OpenAI / flat / litellm-Bedrock keys, so every consumer of the body text sees it: the generic-400 "bare body + large session ⇒ overflow" heuristic in_classify_400,_extract_message(the user-visible message), and the Codex masked-replay check.detailis untouched — it is still read only by_oversized_message_content_rejection.is_client_errorexcludes overflow, the existing entitlement marker from fix(agent): unentitled Codex primary + fallback no longer oscillate or announce a false restore (#106475, salvage #106482) #106549 (agent/turn_api_error.py→fallback_cooldown._mark_entitlement_rejected_model) never ran on large sessions; with the body read correctly it now does.tests/agent/test_error_classifier.py(string + nested shapes not overflow, no compression, fallback, message is the provider text; control: a bareErrorbody on the same session still compresses). Red on base, green on this head.Live probe (real
openaiSDK_make_status_error_from_responseon anhttpx.Response(400)carrying the exact #81558 body → publicclassify_api_error,provider=openai-codex, 223 messages / ~110k tokens, tempHERMES_HOME):8df0a037{"detail": …}400, large sessioncontext_overflow,should_compress=True, messageError code: 400 - {'detail': …}format_error,should_compress=False,should_fallback=True, messageThe 'gpt-5.5-codex' model is not supported when using Codex with a ChatGPT account.format_errorformat_error(unchanged){"detail": {"message": …}}, large sessioncontext_overflowformat_error{"error": {"message": "Error"}}, large session (heuristic control)context_overflowcontext_overflow(unchanged)Root cause:
_body_message_candidatesnever read the FastAPI-style top-leveldetailkey, so a long, descriptive rejection counted as an empty body and tripped the large-session overflow heuristic.Tests:
tests/agent/test_error_classifier.py tests/agent/test_auxiliary_error_classification.py tests/agent/test_entitlement_fail_closed.py→ 177 passed.Refs #81558 (the commenter's "400 + large session ⇒ Context length exceeded … Cannot compress further" loop; the OP's wire-format hypothesis is contradicted in-thread and stays unreproduced)
Refs #106475 (already closed by #106549; this makes that fix reachable on large sessions)
Salvages #100783 (@i-Hun) — slim re-port of its
detail-body half onto the current rule-table classifier; co-authored.Dropped hunks
"session limit"/"weekly limit"usage-limit patterns and the bare"resets "transient signal: a different error class (subscription usage walls), not part of this fix. Re-probed on this head: a 429 carryingYou've hit your session limit · resets 2:20pmalready classifiesrate_limit(retryable) via the status path; the same text as a 400 isformat_error. Left for its own PR if a real 400-shaped report appears._detail_messagelist flattening (loc: msg (type)): the list shape is already consumed by_oversized_message_content_rejectionand needs no message text.Infographic