Surface Flex capacity failures as a distinct terminal error (#47967)

## What changed

- Recognize `flex_unavailable` in HTTP 429 responses and streamed `error` and `response.failed` events. End normal turns without retries and report `Flex capacity unavailable.`
- Expose `flexUnavailable` through the core and app-server protocols and generated schemas and SDK types.
- Keep Flex capacity failures eligible for Guardian review and sampler retries, with a distinct telemetry classification.
- Map unexpected HTTP responses to `HttpConnectionFailed`, preserving their status codes.

## Testing

Add regression coverage for terminal Flex failures over HTTP, SSE, and WebSocket, HTTP 504 status preservation after a stream retry, and protocol serialization. Update Guardian retry and sampler recovery tests to cover Flex failures.

GitOrigin-RevId: f2e03a6d2d36a6d9748c35952e40397f8670d121
This commit is contained in:
Steve Coffey
2026-09-24 23:33:38 +00:00
committed by copyberry
parent 8f103417ef
commit dafb133c5b
39 changed files with 259 additions and 11 deletions
+8
View File
@@ -3,6 +3,7 @@ use codex_client::TransportError;
use codex_http_client::RetryAfter;
use codex_protocol::protocol::MisalignmentErrorDetails;
use http::StatusCode;
use serde_json::Value;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -44,6 +45,8 @@ pub enum ApiError {
message: String,
misalignment: Option<MisalignmentErrorDetails>,
},
#[error("Flex capacity unavailable.")]
FlexUnavailable,
#[error("server overloaded")]
ServerOverloaded { retry_after: Option<RetryAfter> },
}
@@ -53,3 +56,8 @@ impl From<RateLimitError> for ApiError {
Self::RateLimit(err.to_string())
}
}
pub(crate) fn parse_flex_unavailable(error: &Value) -> Option<ApiError> {
(error.get("code").and_then(Value::as_str) == Some("flex_unavailable"))
.then_some(ApiError::FlexUnavailable)
}