#58825·vllm

[Bug]: gpt-oss (HarmonyParser): a stray 'commentary to=assistant' header is returned as a tool call named 'assistant<|channel|>analysis'

Author: redd34Created Sep 26, 2026Updated Sep 29, 2026
Labelstool-callinggpt-oss

Your current environment

Environment (collect_env.py asserts on macOS, so summarised by hand)
OS: macOS 26.5.1 arm64, Python 3.12.13, CPU only
vLLM: 0.30.0 (pure-Python parser code from the manylinux wheel), and main @ 3b4566c5cf014605de6aeab6eb831b4f20511c17
openai-harmony 0.0.8, transformers 5.17.0, torch 2.14.0 (CPU)

Parser-only bug: no GPU, weights or server needed. Tokenizer: openai/gpt-oss-20b@6cee5e81.

Describe the bug

gpt-oss-20b sometimes opens a turn with a stray <|channel|>commentary to=assistant header before its analysis channel. llama.cpp added a rule for it (ggml-org/llama.cpp#21286: "not defined in the Harmony docs, but appears to be a common generation from the 20b variant"). vLLM's Chat Completions HarmonyParser turns that header into a tool call named assistant<|channel|>analysis. The call's arguments are the model's reasoning, and the name is not one of the request's tools.

python
from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
from vllm.parser.parser_manager import ParserManager
from vllm.tokenizers import get_tokenizer

tok = get_tokenizer("openai/gpt-oss-20b", revision="6cee5e81ee83917806bbde320786a8fb61efebee")
tools = [{"type": "function", "function": {"name": "get_weather", "parameters": {
    "type": "object", "properties": {"city": {"type": "string"}}}}}]
req = ChatCompletionRequest(model="m", messages=[{"role": "user", "content": "hi"}],
                            tools=tools, tool_choice="auto")
cls = ParserManager.get_parser("openai", "openai_gptoss", enable_auto_tools=True, is_harmony=True)

out = ("<|channel|>commentary to=assistant<|channel|>analysis<|message|>I'm\nthinking<|end|>"
       "<|start|>assistant<|channel|>final<|message|>Hello, world!\nWhat's up?")
ids = tok.encode(out, add_special_tokens=False)
reasoning, content, calls = cls(tok, req.tools, model_config=None).parse(
    tok.decode(ids, skip_special_tokens=False), req, enable_auto_tools=True, model_output_token_ids=ids)
print("reasoning :", repr(reasoning))
print("content   :", repr(content))
print("tool_calls:", [(c.name, c.arguments) for c in (calls or [])])

Output on 0.30.0 and on main @ 3b4566c (identical):

reasoning : None
content   : "Hello, world!\nWhat's up?"
tool_calls: [('assistant<|channel|>analysis', "I'm\nthinking")]

Streaming (parse_delta, with one token per delta, the whole output in one delta, or random groups) gives the same tool call.

Expected: no tool call. llama.cpp's test for this string (test-chat.cpp L6540-L6546) recovers reasoning="I'm\nthinking" and content="Hello, world!\nWhat's up?". Keeping the content and dropping the reasoning would also be reasonable. A tool call is not.

Where: vllm/parser/harmony.py, _SegmentType.from_channel_and_recipient. It treats any recipient accepted by is_function_recipient as a tool call. On the Chat Completions path that means every bare recipient. The recipient here is assistant<|channel|>analysis: to=assistant addresses the assistant, not a function, and the embedded <|channel|> marker ends up in the name. _normalize_recipient already strips a misparsed <|constrain|> from recipients. Handling <|channel|> the same way, and not treating an assistant recipient as a function, would probably fix this.

Related: #32587 reports <|channel|> leaking into gpt-oss tool names (for example ls<|channel|>commentary) in multi-turn use. This is a single-turn, parser-only repro of the same symptom, and may share a fix. #35906 sanitises recipients on the Responses API path.

Fixture: gpt-oss/llamacpp-stray-commentary-header

I found this with an automated conformance harness. The report was drafted with AI assistance, and I verified the repro above by running it on both versions.

Found by canitoolcall (https://github.com/redd34/canitoolcall), an offline conformance suite for tool-call parsers.

Before submitting a new issue...

  • Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the documentation page, which can answer lots of frequently asked questions.