Empty-body HTTP 400 is surfaced as codexErrorInfo: other with no status or diagnostic
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):
InvalidRequest is not explicitly mapped by to_codex_protocol_error, so it falls through to CodexErrorInfo::Other:
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, notother - 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
- Convert a transport HTTP 400 with
body: Noneand assert that the resulting error has a non-empty message. - Assert that app-server
TurnError.codexErrorInfoisbadRequest(or a structured bad-request variant) rather thanother. - 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.