-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Qwen3-coder Tool Calling Fails with Many Tools via Ollama #6883
Description
Describe the bug
As described in other reports #6655 and #5986, goose and ollama model interactions struggle to do anything useful in developer mode.
Summary
When using Qwen3-coder model through Ollama with Goose's default developer extensions (11 tools), the model fails to execute tool calls. Instead of returning structured JSON tool calls, the model outputs XML-style tool invocations embedded in the text content, which Goose previously could not parse.
Root Cause
Qwen3-coder supports native JSON tool calling when provided with a small number of tools (approximately 5 or fewer). However, when the tool count exceeds this threshold, the model switches behavior and outputs XML-formatted tool calls within the content field of the response, even when the tools array is properly provided in the API request.
Example of XML Output Format
When Qwen3-coder receives too many tools, it outputs tool calls like this:
I'll create the file for you.
<function=developer__text_editor>
<parameter=command>write</parameter>
<parameter=path>/tmp/hello.txt</parameter>
<parameter=file_text>hello world</parameter>
</function>
</tool_call>Instead of the expected JSON format in the tool_calls field:
{
"tool_calls": [{
"id": "call_123",
"function": {
"name": "developer__text_editor",
"arguments": "{\"command\": \"write\", \"path\": \"/tmp/hello.txt\", \"file_text\": \"hello world\"}"
}
}]
}Reproduction Steps
-
Install Goose and configure it to use Ollama with Qwen3-coder:
# ~/.config/goose/config.yaml GOOSE_PROVIDER: ollama GOOSE_MODEL: qwen3-coder:latest
-
Run Goose with the developer extension enabled (default)
-
Ask Goose to perform any file operation, e.g., "Create a file at /tmp/test.txt with the content 'hello'"
-
Observe that Goose responds with text containing XML tool calls but does not actually execute the tool
Technical Details
- Affected Provider: Ollama (using OpenAI-compatible API format)
- Affected Model: qwen3-coder (tested with qwen3-coder:latest)
- Tool Count Threshold: Approximately 5-6 tools triggers the behavior change
- Goose Default Tool Count: 11 tools (with developer extension)
Verification via curl
With few tools (works correctly - returns JSON tool_calls):
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-coder:latest",
"messages": [{"role": "user", "content": "List files in current directory"}],
"tools": [{"type": "function", "function": {"name": "shell", "parameters": {"type": "object", "properties": {"command": {"type": "string"}}}}}]
}'With many tools (fails - returns XML in content):
I'll create the file for you.
<function=developer__text_editor>
<parameter=command>write</parameter>
<parameter=path>/tmp/hello.txt</parameter>
<parameter=file_text>hello world</parameter>
</function>
</tool_call>Solution Implemented - see #6882
Added XML tool call parsing as a fallback in crates/goose/src/providers/formats/openai.rs:
-
parse_xml_tool_callsfunction - Parses XML-style tool calls using regex:- Matches
<function=name>...</function>blocks - Extracts
<parameter=key>value</parameter>pairs - Returns parsed tool calls as
MessageContent::ToolRequest
- Matches
-
Modified
response_to_message- Added fallback logic:- If no JSON
tool_callsfound in response - Check if content contains
<function=pattern - Parse XML and convert to tool requests
- If no JSON
-
Modified
response_to_streaming_message- Added streaming support:- Accumulate text content during streaming
- On stream completion, check for XML tool calls
- Parse and yield tool requests if found
Testing
Unit tests added for:
- Single XML tool call parsing
- Multiple XML tool calls
- XML with prefix text
- Exact Qwen3-coder output format
- JSON tool_calls taking precedence over XML (no false positives)
- Integration with
response_to_message
Files to be changed
crates/goose/src/providers/formats/openai.rs- Add XML parsing logic and tests
Related Information
- This appears to be a limitation/behavior of Qwen3-coder when handling large tool schemas
- Other models using the Ollama OpenAI-compatible endpoint may exhibit similar behavior
- The fix is backward-compatible and only activates when JSON tool_calls are absent
Potential Future Improvements
- Consider adding a configuration option to limit the number of tools sent to models known to have this limitation
- Add logging/telemetry when XML fallback is triggered to help identify affected models
- Investigate if this behavior can be controlled via Ollama configuration or model parameters
Please provide the following information
- OS & Arch: Mac OS Tahoe 26.2 (M1 Ultra 128GB ram), ARM 64
- Interface: UI / CLI
- Version: v1.22.1
- Extensions enabled: apps, developer
- Provider & Model: qwen3-coder:latest - 30b
Additional context
I have a fix for this here #6882