Skip to content

stop as a plain string is rejected, and malformed requests get a 500 instead of a 400 #221

Closed

Description

@HamedShams

Two related gaps on /v1/chat/completions and /v1/completions (main @ 1b6befd, M1 Max):

1. stop as a plain string is rejected

OpenAI documents stop as "string / array / null", and a bare string is what several SDKs and llama.cpp-style clients send. Both request types decode only the array form:

let stop: [String]?

(and L4487 for TextCompletionRequest), so the request fails to decode:

curl -s localhost:5413/v1/chat/completions -H 'Content-Type: application/json' \
  -d '{"messages":[{"role":"user","content":"Count from one to ten in words."}],"max_tokens":40,"stop":"three"}'
# → HTTP 500
# {"error":{"type":"server_error","code":"internal_error","message":"DecodingError.typeMismatch: Expected value of type Array<Any>. Path: stop. …"}}

2. A request that does not decode is answered with 500, not 400

That 500 is the route's generic catch (L1520-L1536 / L1540-L1552), which treats a DecodingError from the request body the same as an internal failure. OpenAI answers these with 400 invalid_request_error and names the offending param. The distinction matters in practice: the OpenAI SDKs treat a 400 as final but retry a 5xx (openai-python retries twice by default), so a client with a malformed request pays three round trips to learn nothing, and the error tells it the server was at fault. Same story for malformed JSON or a missing messages:

POST /v1/chat/completions {"model":"x"}
→ 500 {"error":{"type":"server_error","code":"internal_error","message":"DecodingError.keyNotFound: Key 'messages' not found …"}}

Suggested fix: accept stop as string or array (same try? decode(String)-then-array pattern MessageContent already uses), and map a DecodingError from the body decode to 400 invalid_request_error with param set from the coding path. Only the request-body decode; everything else on these routes stays a 500. I have both ready with unit tests and contract-script assertions, happy to send a PR.

Activity

  1. solderzzc commented on Oct 9, 2026

    @solderzzc
    Member

    Thanks for the detailed report. I checked it against main and agree with both points:

    1. stop is decoded only as an array in ChatRequest / TextCompletionRequest, so a bare string (which OpenAI allows) fails to decode.
    2. The /v1/chat/completions route's generic catch turns any error, including a DecodingError from the request body, into a 500 server_error. OpenAI answers these with 400 invalid_request_error, and SDKs retry 5xx but not 400.

    I have not run your curl repro myself; this is confirmed from the code.

    Your proposed fix looks right: accept stop as string or array, and map only the request-body DecodingError to 400 invalid_request_error with param from the coding path, leaving everything else on these routes as 500. A PR with the unit tests and contract-script assertions would be very welcome.

  2. HamedShams commented on Oct 9, 2026

    @HamedShams
    ContributorAuthor

    Thanks for confirming @solderzzc

    PR is up: #223. stop now decodes as a string or an array on both request types (null or absent still means no client stops), and only a DecodingError from the request-body decode maps to 400 invalid_request_error with param from the coding path, so stop, messages, messages.0.role and so on get named; everything else on those routes still returns 500 as before.

    10 unit tests cover the decoding and the error payload, and the contract script gained 2 assertions: a bare stop string is accepted and honoured and "stop": 42 answers 400 naming stop. Both are red on main and green on the branch, against the CI model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions