Skip to content

[Bugfix] Surface SamplingParams.verify errors as client errors (HTTP 400) - #53357

Draft
khluu wants to merge 1 commit into
mainfrom
fix/params-verify-client-error
Draft

khluu wants to merge 1 commit into
mainfrom
fix/params-verify-client-error

Conversation

@khluu

@khluu khluu commented Aug 22, 2026

Copy link
Copy Markdown
Member

Purpose

Fix the recurring randomized-Schemathesis failures in the H200 Entrypoints Integration (API Server OpenAI - Part 1) CI job: fuzzed POST /inference/v1/generate requests intermittently produced {"error":{"message":"","type":"InternalServerError","param":null,"code":500}} (main builds #84796, #85124, #85154 — the job now fails most runs).

Root cause

SamplingParams.verify() still raises plain ValueError for some request-caused validation failures (e.g. trace_decode_token_ids=[], which passes pydantic and the endpoint's msgspec pre-check). InputProcessor._validate_params runs lazily inside AsyncLLM.generate(), whose exception handling deliberately passes VLLMClientError through — but a plain ValueError falls into the generic branch and is wrapped in a bare EngineGenerateError (async_llm.py), which the entrypoints error mapper classifies as a server error. Result: HTTP 500 with an empty message instead of a 400, and the fuzz check correctly flags every 5xx.

Reproduced deterministically with the exact schemathesis-shrunk payload against the CI image (SmolVLM-256M, the schema-test server config): unpatched → empty 500 with the full EngineGenerateError chain in --log-error-stack; patched → 400 {"message":"trace_decode_token_ids must be a non-empty list.","type":"BadRequestError"}.

Changes

  • InputProcessor._validate_params: convert ValueError from both params.verify() call sites (sampling and pooling) into VLLMValidationError at the validation boundary, so current and future plain-ValueError validations surface as 400s with their real message via the existing except VLLMClientError: raise pass-through.
  • Migrate the two remaining plain ValueError raises in the sampling branch (return_sampling_mask temperature/top_k gates) to VLLMValidationError with parameter/value, matching the rest of the function.
  • Regression test in tests/v1/engine/test_input_processor_trace_replay.py running the real verify() with trace_decode_token_ids=[] and asserting VLLMValidationError; verified it fails without the fix.

Duplicate-work check

Searched open PRs for trace_decode_token_ids, EngineGenerateError 500, and schemathesis inference generate; no open PR addresses this classification bug.

Test plan

  • tests/v1/engine/test_input_processor_trace_replay.py: 6/6 passed (new test fails without the fix).
  • Live reproduction on the CI image (vllm serve HuggingFaceTB/SmolVLM-256M-Instruct with the schema-test args): exact fuzzed payload returns 400 with message after the fix, empty 500 before.
  • All applicable pre-commit hooks pass.
  • Exact CI validation: isolated-branch run of entrypoints-integration-api-server-openai-part-1 (the schemathesis job) to be linked in comments.

AI-assistance disclosure

This change was prepared with AI assistance and reviewed before submission.

…400)

SamplingParams.verify() still raises plain ValueError for some
request-caused validation failures (e.g. trace_decode_token_ids=[]).
Because InputProcessor._validate_params runs lazily inside
AsyncLLM.generate(), such errors fall into the generic exception
branch and get wrapped in a bare EngineGenerateError, so the API
server returns HTTP 500 with an empty message instead of a 400.

The schemathesis fuzz job (H200 Entrypoints OpenAI Part 1) found this
repeatedly through POST /inference/v1/generate: any fuzzed
sampling_params tripping a plain-ValueError validation produced
{"error":{"message":"","type":"InternalServerError",...,"code":500}}
(main builds 84796, 85124, 85154).

Convert ValueError from both params.verify() call sites into
VLLMValidationError at the _validate_params boundary, and migrate the
two remaining plain ValueError raises in the sampling branch. Client
errors then take the existing 'except VLLMClientError: raise'
pass-through in AsyncLLM.generate and map to 400 with the real message.

Verified live against the exact fuzzed payload on the CI image: the
request now returns 400 'trace_decode_token_ids must be a non-empty
list.' instead of the empty 500.

Co-authored-by: Sherlock <sherlock@agents.local>
Signed-off-by: Kevin Luu <51931015+khluu@users.noreply.github.com>
@mergify mergify Bot added the bug Something isn't working label Aug 22, 2026
@khluu

khluu commented Aug 22, 2026

Copy link
Copy Markdown
Member Author

Exact CI validation is terminal green: isolated build #85158 at head aed5b715 (exact PR metadata, only the schemathesis job) — :nvidia: (H200) Entrypoints Integration (API Server OpenAI - Part 1) passed at 06:58 UTC, including the full test_openapi_stateless fuzz phase that failed in main builds #84796/#85124/#85154.

Caveat stated plainly: the fuzz is randomized, so a single green run is supporting rather than conclusive evidence — the conclusive part is the deterministic live reproduction in the PR description (exact shrunk payload: empty 500 before the fix, 400 BadRequestError with the real validation message after, on the CI image and schema-test server config).

@noooop

noooop commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Are you suggesting that exception handling exhibits a degree of nondeterminism?

Our entrypoints appear to be haunted. LOL....

e.g.

It succeeded on the retry.
┓( ´∀` )┏

@AndreasKaratzas

Copy link
Copy Markdown
Member

Just closed #54402 in favor of this PR.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants