language_model_core: Classify Anthropic prompt-too-long 400s as context overflow (#63596)

Anthropic reports a context-window overflow with HTTP 400 and a message
of the form `prompt is too long: N tokens > M maximum`. The
`BAD_REQUEST` arm of `ProviderErrorCategory::from_http_status` only
recognized OpenAI's phrasings (`context_length_exceeded`, "exceeds the
context window"), and `parse_prompt_too_long` only ran for HTTP 413, so
these rejections were classified as `InvalidRequest` instead of
`PromptTooLarge`. This affects every wire path that classifies by
upstream HTTP status, including Zed cloud's `upstream_http_400` code,
and it silently disables any downstream handling keyed on the
`PromptTooLarge` category. This change sniffs the Anthropic phrasing on
400 responses too, carrying the provider-reported token count.

Closes AI-462

Release Notes:

- Fixed Anthropic "prompt is too long" errors reported with HTTP 400 not
being recognized as context-window overflows.
This commit is contained in:
Richard Feldman authored and GitHub committed 2026-09-02 14:47:22 +00:00
1 parent ca3f453f79
commit e75564a92a
1 file changed
+12 -4
@@ -148,9 +148,18 @@ impl ProviderErrorCategory {
StatusCode::BAD_REQUEST if is_invalid_encrypted_content_message(message) => {
Self::InvalidEncryptedContent
}
StatusCode::BAD_REQUEST if is_context_window_exceeded_message(message) => {
Self::PromptTooLarge { tokens: None }
}
// Anthropic reports a context-window overflow as HTTP 400 with a
// "prompt is too long" message rather than HTTP 413, so a
// bad request must be sniffed for both providers' phrasings.
StatusCode::BAD_REQUEST => match parse_prompt_too_long(message) {
Some(tokens) => Self::PromptTooLarge {
tokens: Some(tokens),
},
None if is_context_window_exceeded_message(message) => {
Self::PromptTooLarge { tokens: None }
}
None => Self::InvalidRequest,
},
StatusCode::UNAUTHORIZED => Self::Authentication,
StatusCode::FORBIDDEN => Self::Permission,
StatusCode::NOT_FOUND => Self::EndpointNotFound,
@@ -158,7 +167,6 @@ impl ProviderErrorCategory {
StatusCode::PAYLOAD_TOO_LARGE => Self::PromptTooLarge {
tokens: parse_prompt_too_long(message),
},
StatusCode::BAD_REQUEST => Self::InvalidRequest,
StatusCode::CONFLICT => Self::Conflict,
StatusCode::REQUEST_TIMEOUT | StatusCode::GATEWAY_TIMEOUT => Self::Timeout,
StatusCode::TOO_MANY_REQUESTS => Self::RateLimit,