Repository navigation
qwen3_coder tool parser rejects float-formatted integer args ("140.0"), silently dropping the call and causing client infinite loops #1627
Description
qwen3_coder tool parser rejects float-formatted integer args ("140.0"), and server silently drops the call — causing infinite client loops
Summary
Two compounding problems, observed with mlx_lm.server 0.31.3 serving a Qwen3-Coder variant to an agentic client (opencode):
-
tool_parsers/qwen3_coder.py:_convert_param_valuehandles schema-typed integers with a bareint(param_value). Qwen3-Coder sometimes emits float-formatted integers (e.g.140.0for alimitparameter typedinteger), andint("140.0")raisesValueError. The float branch immediately below already coerces tolerantly in both directions; the int branch does not. -
server.py(ToolCallFormatter.__call__): when the parser raises, the tool call is dropped with only a server-side log warning, and the response is returned as HTTP 200 withfinish_reason: "tool_calls"but no tool call. The client gets no error signal at all.
The combination is worse than either bug alone: an agentic client sees a tool-call turn with no tool call, retries, and — with greedy/deterministic sampling over an identical (fully prompt-cached) context — the model regenerates the exact same rejected output. The result is a tight infinite loop. In our case the client looped ~390 times at ~1.3s per iteration (55 tokens generated, 56k tokens cache-read each time) until manually killed:
2026-07-28 10:15:28,688 - WARNING - Failed to parse tool call (ValueError: invalid literal for int() with base 10: '140.0') — tool text was likely truncated mid-generation.
127.0.0.1 - - [28/Jul/2026 10:15:28] "POST /v1/chat/completions HTTP/1.1" 200 -
2026-07-28 10:15:30,026 - WARNING - Failed to parse tool call (ValueError: invalid literal for int() with base 10: '140.0') — tool text was likely truncated mid-generation.
127.0.0.1 - - [28/Jul/2026 10:15:30] "POST /v1/chat/completions HTTP/1.1" 200 -
... (repeats indefinitely)
Note the warning text ("tool text was likely truncated mid-generation") is misleading here — the tool text was complete and well-formed XML; only the type coercion failed.
Reproduction
from mlx_lm.tool_parsers.qwen3_coder import parse_tool_call
tools = [{"type": "function", "function": {"name": "read", "parameters": {
"properties": {"filePath": {"type": "string"}, "limit": {"type": "integer"}}}}}]
parse_tool_call(
"<function=read>\n<parameter=filePath>\n/x/y.py\n</parameter>\n"
"<parameter=limit>\n140.0\n</parameter>\n</function>",
tools,
)
# ValueError: invalid literal for int() with base 10: '140.0'Suggested fixes
Parser — make the integer branch tolerant, mirroring the existing float branch:
try:
return int(param_value)
except ValueError:
try:
float_value = float(param_value)
except ValueError:
return param_value # let downstream schema validation give the model feedback
return int(float_value) if float_value.is_integer() else float_valueWe've been running this patch locally; it resolved the loop with no regressions (plain ints unchanged, fractional values pass through as floats, non-numeric values fall back to the raw string).
Server — consider surfacing parse failures to the client instead of silently dropping the call (e.g. return the raw tool text as content, or an error), so deterministic clients can't loop invisibly on a permanently-failing parse.
Environment
- mlx-lm 0.31.3 (
mlx_lm.server --model <Qwen3-Coder-Next mxfp8 MLX quant> --host 127.0.0.1 --port 8081) - macOS (Apple silicon), Python 3.12
- Client: opencode 1.18.8 (OpenAI-compatible chat completions with tools)
- Bug confirmed still present on current
main(tool_parsers/qwen3_coder.py, bareint(param_value))
Activity
fix(qwen3_coder): accept float-formatted integers in tool-call args
Fix 5 more GPU-independent issues: rope config, batch caches, tool pa…
zcbenz commentedon Aug 22, 2026
Fixed by #1417