Qwen3VLChatSupportedMmLimits() (include/vllm/entrypoints/openai/chat_mm.h) declares the
Qwen3-VL image chat seam's honest ceiling as {"image": 1} and every other modality absent,
because MakeQwen3VLImageChatFn locates exactly one image part and routes no video or audio.
Upstream declares both unlimited (get_supported_mm_limits, inherited from
Qwen2VLProcessingInfo, qwen2_vl.py:851-852 → {"image": None, "video": None}), so this is a
deliberate, recorded narrowing to what is actually implemented.
The number is right. The message is not sufficient.
A request carrying two images is refused with upstream's own text:
At most 1 image(s) may be provided in one prompt.
and a video part with:
At most 0 video(s) may be provided in one prompt.
Both are correct mirrors of context.py's message, and both are indistinguishable from a
deployment that simply configured --limit-mm-per-prompt '{"image": 1, "video": 0}'. A user
who hits the second one cannot tell "your operator lowered this limit, raise it" from "this
engine has no video arm at all, and no flag will give you one". AGENTS.md requires that an
unimplemented arm "is refused with a message naming the missing piece"; the PR that landed
wave L2 (#749) claimed the ceiling satisfies that requirement, and the review round found it
does not. The claim is corrected in the header; this issue owes the behaviour.
Why it was not fixed in the same flow. The fix is a divergence from upstream's exact
message text, which the mirror rule makes a design decision rather than a repair: the message
is ported verbatim and three suites assert it byte-for-byte
(test_processing_limits, test_chat_mm, test_openai_api_server). The plausible shapes —
appending a vllm.cpp-specific clause only when the limit came from an ABSENT supported-modality
entry rather than from configuration, or carrying the distinction in the error body's type
/ param fields instead of the message — differ in what an OpenAI client sees, so this wants
its own spec and its own fresh review rather than being folded into a review repair.
Note the machinery for the distinction already exists and is already used: ValidateNumItems
(src/vllm/multimodal/processing/context.cpp) appends
Set --limit-mm-per-prompt to increase this limit. only when num_items <= supported_limit
— i.e. only when raising the configured limit would actually help. The absence of that hint is
today the only signal that the arm is unimplemented, and it is a signal by omission, which is
exactly what "named, never left to be discovered" rules out.
Scope: the message/param shape for a limit that comes from an unimplemented arm, plus the port
of whatever upstream field carries it, plus tests. Related: #607 (wave L2), #686, PR #749.
Qwen3VLChatSupportedMmLimits()(include/vllm/entrypoints/openai/chat_mm.h) declares theQwen3-VL image chat seam's honest ceiling as
{"image": 1}and every other modality absent,because
MakeQwen3VLImageChatFnlocates exactly one image part and routes no video or audio.Upstream declares both unlimited (
get_supported_mm_limits, inherited fromQwen2VLProcessingInfo,qwen2_vl.py:851-852→{"image": None, "video": None}), so this is adeliberate, recorded narrowing to what is actually implemented.
The number is right. The message is not sufficient.
A request carrying two images is refused with upstream's own text:
and a video part with:
Both are correct mirrors of
context.py's message, and both are indistinguishable from adeployment that simply configured
--limit-mm-per-prompt '{"image": 1, "video": 0}'. A userwho hits the second one cannot tell "your operator lowered this limit, raise it" from "this
engine has no video arm at all, and no flag will give you one". AGENTS.md requires that an
unimplemented arm "is refused with a message naming the missing piece"; the PR that landed
wave L2 (#749) claimed the ceiling satisfies that requirement, and the review round found it
does not. The claim is corrected in the header; this issue owes the behaviour.
Why it was not fixed in the same flow. The fix is a divergence from upstream's exact
message text, which the mirror rule makes a design decision rather than a repair: the message
is ported verbatim and three suites assert it byte-for-byte
(
test_processing_limits,test_chat_mm,test_openai_api_server). The plausible shapes —appending a vllm.cpp-specific clause only when the limit came from an ABSENT supported-modality
entry rather than from configuration, or carrying the distinction in the error body's
type/
paramfields instead of the message — differ in what an OpenAI client sees, so this wantsits own spec and its own fresh review rather than being folded into a review repair.
Note the machinery for the distinction already exists and is already used:
ValidateNumItems(
src/vllm/multimodal/processing/context.cpp) appendsSet--limit-mm-per-promptto increase this limit.only whennum_items <= supported_limit— i.e. only when raising the configured limit would actually help. The absence of that hint is
today the only signal that the arm is unimplemented, and it is a signal by omission, which is
exactly what "named, never left to be discovered" rules out.
Scope: the message/param shape for a limit that comes from an unimplemented arm, plus the port
of whatever upstream field carries it, plus tests. Related: #607 (wave L2), #686, PR #749.