mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Treat model-not-found provider errors as terminal failures (#56445)
## Why Provider errors with code `model_not_found` should end the turn immediately and preserve the provider's message instead of triggering retries. ## What changed - Recognize `model_not_found` in HTTP 400, 403, and 404 responses and in streamed `error` and `response.failed` events. - Preserve the provider message and any available HTTP status, and expose `modelNotFound` through the app-server protocol and generated schemas and SDK types. ## Testing Add coverage for HTTP classification, statusless stream errors, and terminal failures without retries across HTTP, SSE, and WebSocket responses. Verify that `thread/read` preserves the error reported by the live notification. GitOrigin-RevId: 88f824ac03c8c9669915b1e40c67b76c08326fbe
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::TransportError;
|
||||
use crate::error::ApiError;
|
||||
use crate::error::parse_flex_unavailable;
|
||||
use crate::error::parse_terminal_provider_error;
|
||||
use crate::rate_limits::parse_promo_message;
|
||||
use crate::rate_limits::parse_rate_limit_for_limit;
|
||||
use crate::rate_limits::parse_rate_limit_reached_type;
|
||||
@@ -36,6 +36,7 @@ pub fn map_api_error(err: ApiError) -> CodexErr {
|
||||
| ApiError::FlexUnavailable
|
||||
| ApiError::RateLimit(_)
|
||||
| ApiError::InvalidRequest { .. }
|
||||
| ApiError::ModelNotFound { .. }
|
||||
| ApiError::InvalidPrompt { .. }
|
||||
| ApiError::CyberPolicy { .. }
|
||||
| ApiError::BioPolicy { .. }
|
||||
@@ -78,6 +79,9 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
|
||||
})
|
||||
}
|
||||
ApiError::InvalidRequest { message } => CodexErr::InvalidRequest(message),
|
||||
ApiError::ModelNotFound { message, status } => {
|
||||
CodexErr::new(CodexErrorDetails::ModelNotFound { message, status })
|
||||
}
|
||||
ApiError::InvalidPrompt { message } => {
|
||||
CodexErr::new(CodexErrorDetails::InvalidPrompt { message })
|
||||
}
|
||||
@@ -101,10 +105,14 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
|
||||
..
|
||||
} => {
|
||||
let body_text = body.unwrap_or_default();
|
||||
let parsed_body = serde_json::from_str::<Value>(&body_text).ok();
|
||||
let provider_error = parsed_body.as_ref().and_then(|body| body.get("error"));
|
||||
if let Some(error) = parse_terminal_provider_error(provider_error, Some(status)) {
|
||||
return map_api_error_details(error);
|
||||
}
|
||||
|
||||
if status == http::StatusCode::SERVICE_UNAVAILABLE
|
||||
&& let Ok(value) = serde_json::from_str::<serde_json::Value>(&body_text)
|
||||
&& let Some(error) = value.get("error")
|
||||
&& let Some(error) = provider_error
|
||||
{
|
||||
match error.get("code").and_then(Value::as_str) {
|
||||
Some("server_is_overloaded") => return CodexErr::ServerOverloaded,
|
||||
@@ -123,8 +131,7 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
|
||||
|
||||
if (status == http::StatusCode::BAD_REQUEST
|
||||
|| status == http::StatusCode::FORBIDDEN)
|
||||
&& let Ok(parsed) = serde_json::from_str::<Value>(&body_text)
|
||||
&& let Some(error) = parsed.get("error")
|
||||
&& let Some(error) = provider_error
|
||||
&& error.get("code").and_then(Value::as_str)
|
||||
== Some(MISALIGNMENT_POLICY_VIOLATION_ERROR_CODE)
|
||||
{
|
||||
@@ -145,8 +152,7 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
|
||||
}
|
||||
|
||||
if status == http::StatusCode::BAD_REQUEST {
|
||||
if let Ok(parsed) = serde_json::from_str::<Value>(&body_text)
|
||||
&& let Some(error) = parsed.get("error")
|
||||
if let Some(error) = provider_error
|
||||
&& let Some(
|
||||
code @ (CYBER_POLICY_ERROR_CODE
|
||||
| BIO_POLICY_ERROR_CODE
|
||||
@@ -183,11 +189,6 @@ fn map_api_error_details(err: ApiError) -> CodexErr {
|
||||
} else if status == http::StatusCode::INTERNAL_SERVER_ERROR {
|
||||
CodexErr::InternalServerError
|
||||
} else if status == http::StatusCode::TOO_MANY_REQUESTS {
|
||||
if let Ok(body) = serde_json::from_str::<Value>(&body_text)
|
||||
&& let Some(error) = body.get("error").and_then(parse_flex_unavailable)
|
||||
{
|
||||
return map_api_error(error);
|
||||
}
|
||||
if let Ok(err) = serde_json::from_str::<UsageErrorResponse>(&body_text) {
|
||||
if err.error.error_type.as_deref() == Some("usage_limit_reached") {
|
||||
let limit_id = extract_header(headers.as_ref(), ACTIVE_LIMIT_HEADER);
|
||||
|
||||
Reference in New Issue
Block a user