Empty-body HTTP 400 is surfaced as codexErrorInfo: other with no status or diagnostic

Open 💬 0 comments Opened Jul 13, 2026 by andrew-stelmach-fleet

Summary

When the model API returns HTTP 400 with an empty response body, Codex app-server emits a terminal TurnError with no actionable diagnostic:

{"message":"","codexErrorInfo":"other","additionalDetails":null}

Clients cannot distinguish an invalid request, context overflow, malformed payload, or another bad-request condition. Retrying blindly can repeat a deterministic terminal failure.

This was observed while investigating the context-overflow/compaction failure reported in #32888, but the error-classification issue is independent and applies to any empty-body 400.

Current conversion path

For a 400 response, body.unwrap_or_default() produces an empty string and the generic branch creates CodexErr::InvalidRequest(body_text):

https://github.com/openai/codex/blob/bc8222b8d9e44377a3d7c7b7970e32e7c29ec34f/codex-rs/codex-api/src/api_bridge.rs#L63-L82

InvalidRequest is not explicitly mapped by to_codex_protocol_error, so it falls through to CodexErrorInfo::Other:

https://github.com/openai/codex/blob/bc8222b8d9e44377a3d7c7b7970e32e7c29ec34f/codex-rs/protocol/src/error.rs#L242-L250

The original HTTP status is also discarded because InvalidRequest stores only a string.

Expected behavior

The app-server error should preserve enough structured information for a client to make a correct retry/reporting decision. At minimum:

  • classify this as badRequest, not other
  • preserve httpStatusCode: 400
  • provide a non-empty fallback message such as Request failed with HTTP 400 and an empty response body
  • preserve request/correlation identifiers when available

If an empty body prevents determining the semantic subtype, that uncertainty should be explicit while retaining the known transport facts.

Suggested regression tests

  1. Convert a transport HTTP 400 with body: None and assert that the resulting error has a non-empty message.
  2. Assert that app-server TurnError.codexErrorInfo is badRequest (or a structured bad-request variant) rather than other.
  3. Assert that the HTTP status survives through the core error and app-server protocol conversion.

Possible fix direction

Either extend InvalidRequest to carry HTTP metadata or introduce a structured bad-request error for transport responses. Explicitly map CodexErr::InvalidRequest to CodexErrorInfo::BadRequest, and synthesize a fallback message when the body is empty.

View original on GitHub ↗