Skip to content

fix(translation): fail-closed on malformed Chat Completions success - #869

Open
leseb wants to merge 1 commit into
praxis-proxy:mainfrom
leseb:leseb/validate-issue-804
Open

fix(translation): fail-closed on malformed Chat Completions success#869
leseb wants to merge 1 commit into
praxis-proxy:mainfrom
leseb:leseb/validate-issue-804

Conversation

@leseb

@leseb leseb commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

The Responses translator accepted any HTTP 200 body as a successful Chat Completions response, so a malformed payload like {} became a counterfeit completed Responses object (status: "completed", error: null, output: []) that silently hid the upstream failure. This change validates the minimum successful shape before translating — a non-empty choices array, a first choice object, a supported finish_reason (stop/length/tool_calls/content_filter), an assistant message, and at least one translatable output (content, refusal, or a well-formed function call, with tool_calls finish reasons requiring function calls) — so malformed responses now fail closed as HTTP 500 instead of a fake success. It is the smallest complete change: the check lives in the shared translator that every finite HTTP 200 path already calls, and well-formed responses are unaffected.

Related issue

Closes #804

Validation

  • Unit tests — cargo test -p praxis-ai-apis -- translation responses_to_chat_completions (138 passed; 5 new/updated shape-rejection tests confirmed by exact name)
  • Integration or functional tests — cargo test -p praxis-tests-integration -- responses_to_chat_completions (7 passed, incl. new responses_to_chat_completions_rejects_malformed_finite_success asserting a finite HTTP 200 {} → 500)
  • make lint (clippy -D warnings, fmt, deps, docs, example, and README-sync checks all green)
  • make build

Checklist

  • I reviewed every changed line and can explain the change.
  • New capabilities include an example config and functional example test. (No new capability — a bug fix; added a functional example test to the existing responses_to_chat_completions example config.)
  • User-facing behavior and generated documentation are updated. (Malformed finite success now returns 500; generated docs remain in sync per make lint README-sync checks — no doc changes needed.)
  • Performance-sensitive changes include appropriate benchmark or load-test evidence. (N/A — validation is a bounded check over an already-parsed value.)
  • Commits are signed and include a Signed-off-by trailer.

Breaking changes

Narrow behavioral change on an error path: a backend returning HTTP 200 with a body that does not conform to the Chat Completions success schema previously produced a completed Responses object with empty output; it now fails closed with HTTP 500. Well-formed responses are unaffected and no migration is required.

The Responses translator accepted any JSON object as a successful Chat
Completions response. A backend returning HTTP 200 with a malformed body
such as `{}` was translated into a fake successful Responses object
(`status: "completed"`, `error: null`, `output: []`), hiding the upstream
failure from the client.

Validate the minimum successful shape before translating: a non-empty
`choices` array, a first choice object, a supported `finish_reason`
(stop/length/tool_calls/content_filter), an assistant `message`, and at
least one translatable output (content, refusal, or a well-formed
function tool call). A `tool_calls` finish reason now requires function
tool calls. Malformed responses fail closed as HTTP 500 instead of
surfacing a counterfeit success.

Add unit coverage for each rejected shape, a filter-boundary test that
aborts after headers are sent, and an end-to-end regression asserting a
finite HTTP 200 `{}` becomes a 500.

Closes praxis-proxy#804

Signed-off-by: Sébastien Han <seb@redhat.com>
@leseb
leseb requested review from a team and jland-redhat September 2, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Malformed successful Chat Completions responses become completed Responses resources

1 participant