vllm - 💡(How to fix) Fix [Bug]: GLM chat template crashes with "'None' has no attribute 'items'" when tool_call arguments is the string "null" [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

Note: sending arguments: null (JSON null, not string) is correctly rejected by Pydantic with a different error. Only the string "null" hits this path. import json, urllib.request, urllib.error, os except urllib.error.HTTPError as e: Error: HTTP 400: {"error": {"message": "'None' has no attribute 'items'", "type": "BadRequestError", "param": null, "code": 400}}

Fix Action

Fixed

Code Example

vLLM v0.20.1 confirmed affected.
Model: zai-org/GLM-5.1-FP8
Startup flags: --tool-call-parser glm47 --reasoning-parser glm45 --enable-auto-tool-choice --chat-template-content-format string

---

import json, urllib.request, urllib.error, os

payload = {
    "model": os.environ["VLLM_MODEL"],
    "messages": [
        {"role": "user", "content": "What time is it?"},
        {
            "role": "assistant",
            "content": None,
            "tool_calls": [{
                "id": "call_1",
                "type": "function",
                "function": {"name": "get_current_time", "arguments": "null"},
            }],
        },
        {"role": "tool", "tool_call_id": "call_1", "content": "14:32 UTC"},
    ],
    "tools": [{
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Returns the current server time.",
            "parameters": {"type": "object", "properties": {}, "required": []},
        },
    }],
    "max_tokens": 64,
}

req = urllib.request.Request(
    f"{os.environ['VLLM_BASE_URL']}/v1/chat/completions",
    data=json.dumps(payload).encode(),
    headers={"Content-Type": "application/json", "Authorization": f"Bearer {os.environ['VLLM_API_KEY']}"},
    method="POST",
)
try:
    urllib.request.urlopen(req, timeout=30)
except urllib.error.HTTPError as e:
    print(e.read().decode())

---

HTTP 400: {"error": {"message": "'None' has no attribute 'items'", "type": "BadRequestError", "param": null, "code": 400}}
RAW_BUFFERClick to expand / collapse

Your current environment

<details> <summary>The output of <code>python collect_env.py</code></summary>
vLLM v0.20.1 confirmed affected.
Model: zai-org/GLM-5.1-FP8
Startup flags: --tool-call-parser glm47 --reasoning-parser glm45 --enable-auto-tool-choice --chat-template-content-format string
</details>

🐛 Describe the bug

When a multi-turn request includes an assistant message whose tool_calls[].function.arguments is the JSON string "null", vLLM returns a 400 BadRequestError: 'None' has no attribute 'items'.

The request is valid per the OpenAI wire spec — arguments is a string, so it passes Pydantic validation — but the server crashes before producing a response. This surfaces in real chatbot applications where the model calls a zero-argument tool: the client echoes the assistant turn back verbatim on the next round, and arguments ends up as the string "null".

Note: sending arguments: null (JSON null, not string) is correctly rejected by Pydantic with a different error. Only the string "null" hits this path.

Minimal reproduction (no model needed, pure HTTP):

import json, urllib.request, urllib.error, os

payload = {
    "model": os.environ["VLLM_MODEL"],
    "messages": [
        {"role": "user", "content": "What time is it?"},
        {
            "role": "assistant",
            "content": None,
            "tool_calls": [{
                "id": "call_1",
                "type": "function",
                "function": {"name": "get_current_time", "arguments": "null"},
            }],
        },
        {"role": "tool", "tool_call_id": "call_1", "content": "14:32 UTC"},
    ],
    "tools": [{
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "Returns the current server time.",
            "parameters": {"type": "object", "properties": {}, "required": []},
        },
    }],
    "max_tokens": 64,
}

req = urllib.request.Request(
    f"{os.environ['VLLM_BASE_URL']}/v1/chat/completions",
    data=json.dumps(payload).encode(),
    headers={"Content-Type": "application/json", "Authorization": f"Bearer {os.environ['VLLM_API_KEY']}"},
    method="POST",
)
try:
    urllib.request.urlopen(req, timeout=30)
except urllib.error.HTTPError as e:
    print(e.read().decode())

Error:

HTTP 400: {"error": {"message": "'None' has no attribute 'items'", "type": "BadRequestError", "param": null, "code": 400}}

Before submitting a new issue...

  • Searched existing issues — closest match is #32213 (parser crash on zero-arg tool output), which is a different code path. No existing issue covers this input path.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING