Handle context length errors consistently across response transports (#60482)

## Why

HTTP 400 responses with `context_length_exceeded` need to reach context-window recovery so manual compaction can retry with less history. Streaming context-window failures should end the stream immediately.

## What changed

- Map `context_length_exceeded` through the shared terminal provider error parser for HTTP 400 responses and events without an HTTP status.
- Return SSE context-window failures immediately.
- Preserve retryable handling for WebSocket errors carrying HTTP 503, even when their code is `context_length_exceeded`.

## Testing

Add coverage for WebSocket status handling and immediate SSE termination with retry headers. Update the manual compaction test to verify recovery from an HTTP 400 context-window error by dropping one history item and retrying.

GitOrigin-RevId: 56d8c831c2fad2ca1318318a3b4258fcc516d689
This commit is contained in:
Charlie Marsh
2026-11-10 00:55:34 +00:00
committed by copyberry
parent d9446b4b01
commit 1885417cd4
6 changed files with 46 additions and 10 deletions
@@ -1326,6 +1326,32 @@ mod tests {
assert!(body.contains("Model does not support image inputs"));
}
#[test]
fn wrapped_context_length_error_respects_http_status() {
for status in [400, 503] {
let payload = json!({
"type": "error",
"status": status,
"error": {"code": "context_length_exceeded"}
})
.to_string();
let event = parse_wrapped_websocket_error_event(&payload).unwrap();
let error = map_wrapped_websocket_error_event(event, payload).unwrap();
let error = crate::api_bridge::map_api_error(error);
assert_eq!(
matches!(
error.details(),
codex_protocol::error::CodexErrorDetails::ContextWindowExceeded
),
status == 400
);
assert_eq!(
error.retry_delay(/*retry_count*/ 1).is_some(),
status == 503
);
}
}
#[test]
fn parse_wrapped_websocket_error_event_with_connection_limit_maps_retryable() {
let payload = json!({