I have searched the existing issues and discussions.
I am using the latest version of Open WebUI.
Installation Method
Docker
Open WebUI Version
v0.6.13
Ollama Version (if applicable)
0.9.0
Operating System
macOS Sequoia (15.5)
Browser (if applicable)
Firefox 139.0.1
Confirmation
I have read and followed all instructions in README.md.
I am using the latest version of both Open WebUI and Ollama.
I have included the browser console logs.
I have included the Docker container logs.
I have provided every relevant configuration, setting, and environment variable used in my setup.
I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
Start with the initial platform/version/OS and dependencies used,
Specify exact install/launch/configure commands,
List URLs visited, user input (incl. example values/emails/passwords if needed),
Describe all options and toggles enabled or changed,
Include any files or environmental changes,
Identify the expected and actual result at each stage,
Ensure any reasonably skilled user can follow and hit the same issue.
Expected Behavior
When the Ollama API returns multiple tools to run, Open WebUI calls each tool one by one, like for the OpenAI-compatible backends
Actual Behavior
The Open WebUI middleware concatenates all tool calls together when using the Ollama backend, resulting in a single invalid tool. In addition, because the tool does not exist, the Open WebUI frontend loads indefinitely without ever performing a request to the tool server (which might be related to #14577)
Steps to Reproduce
Install the latest version of ollama
Download a model compatible with tool-calling (in this example, devstral:latest)
Run the mcp-time server using mcpo:
docker run -d -p 8000:8000 ghcr.io/open-webui/mcpo:main -- uvx mcp-server-time --local-timezone=America/New_York
Open a new chat with the tool-calling model, configure Function Calling as "native" and perform a request which requires calling multiple tools (or the same tool multiple time) such as "What time is it in Paris and New-York?"
Logs & Screenshots
Example of invalid tool call using the Ollama backend:
Associated Open WebUI response log:
{
"model": "devstral:latest",
"stream": true,
"tools": [
{
"type": "function",
"function": {
"type": "function",
"name": "tool_get_current_time_post",
"description": "Get current time in a specific timezones",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
}
},
"required": [
"timezone"
]
}
}
},
{
"type": "function",
"function": {
"type": "function",
"name": "tool_convert_time_post",
"description": "Convert time between timezones",
"parameters": {
"type": "object",
"properties": {
"source_timezone": {
"type": "string",
"description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
},
"time": {
"type": "string",
"description": "Time to convert in 24-hour format (HH:MM)"
},
"target_timezone": {
"type": "string",
"description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
}
},
"required": [
"source_timezone",
"time",
"target_timezone"
]
}
}
}
],
"messages": [
{
"role": "user",
"content": "What time is it in Paris and New York?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_9edcccec-beb6-4bb3-a135-b898f9f87f6a",
"type": "function",
"function": {
"name": "tool_get_current_time_posttool_get_current_time_post",
"arguments": "{\"timezone\": \"Europe/Paris\"}{\"timezone\": \"America/New_York\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_9edcccec-beb6-4bb3-a135-b898f9f87f6a",
"content": null
}
]
}
Performing the request manually using curl:
$ curl -s http://localhost:11434/api/chat -d '{
"model": "devstral",
"messages": [
{
"role": "user",
"content": "What time is it in Paris and New York"
}
],
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "tool_get_current_time_post",
"description": "Get current time in a specific timezones",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
}
},
"required": ["timezone"]
}
}
},
{
"type": "function",
"function": {
"name": "tool_convert_time_post",
"description": "Convert time between timezones",
"parameters": {
"type": "object",
"properties": {
"source_timezone": {
"type": "string",
"description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
},
"time": {
"type": "string",
"description": "Time to convert in 24-hour format (HH:MM)"
},
"target_timezone": {
"type": "string",
"description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
}
},
"required": ["source_timezone", "time", "target_timezone"]
}
}
}
]
}' | jq
From the manual curl request in pasted above, you can see that the Ollama tool_calls array does not contain an index parameter. As such, it always defaults to 0 in convert_ollama_tool_call_to_openai. Because there are multiple tools, they all end up sharing the same index, which is why they are later concatenated together by stream_body_handler.
I believe the fix would be to change the convert_ollama_tool_call_to_openai function so that it increments the default value for index and makes sure there are no collisions
Originally created by @JRomainG on GitHub (Jun 1, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/14583
### Check Existing Issues
- [x] I have searched the existing issues and discussions.
- [x] I am using the latest version of Open WebUI.
### Installation Method
Docker
### Open WebUI Version
v0.6.13
### Ollama Version (if applicable)
0.9.0
### Operating System
macOS Sequoia (15.5)
### Browser (if applicable)
Firefox 139.0.1
### Confirmation
- [x] I have read and followed all instructions in `README.md`.
- [x] I am using the latest version of **both** Open WebUI and Ollama.
- [x] I have included the browser console logs.
- [x] I have included the Docker container logs.
- [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.**
- [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
- [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps:
- Start with the initial platform/version/OS and dependencies used,
- Specify exact install/launch/configure commands,
- List URLs visited, user input (incl. example values/emails/passwords if needed),
- Describe all options and toggles enabled or changed,
- Include any files or environmental changes,
- Identify the expected and actual result at each stage,
- Ensure any reasonably skilled user can follow and hit the same issue.
### Expected Behavior
When the Ollama API returns multiple tools to run, Open WebUI calls each tool one by one, like for the OpenAI-compatible backends
### Actual Behavior
The Open WebUI middleware concatenates all tool calls together when using the Ollama backend, resulting in a single invalid tool. In addition, because the tool does not exist, the Open WebUI frontend loads indefinitely without ever performing a request to the tool server (which might be related to #14577)
### Steps to Reproduce
1. Install the latest version of ollama
2. Download a model compatible with tool-calling (in this example, [`devstral:latest`](https://ollama.com/library/devstral:latest))
3. Run the mcp-time server using mcpo:
```sh
docker run -d -p 8000:8000 ghcr.io/open-webui/mcpo:main -- uvx mcp-server-time --local-timezone=America/New_York
```
4. Run Open WebUI using docker with debug logs:
```sh
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -e GLOBAL_LOG_LEVEL=DEBUG -v open-webui:/app/backend/data ghcr.io/open-webui/open-webui:v0.6.13
```
5. Configure the Ollama backend in Open WebUI
6. Configure the mcp-time tool server in Open WebUI
7. Open a new chat with the tool-calling model, configure Function Calling as "native" and perform a request which requires calling multiple tools (or the same tool multiple time) such as "What time is it in Paris and New-York?"
### Logs & Screenshots
Example of invalid tool call using the Ollama backend:
<img width="1512" alt="Ollama backend multi-tool-calling bug example" src="https://github.com/user-attachments/assets/e5a712c3-46d0-4c74-ba1f-0a04293d9470" />
Associated Open WebUI response log:
<details>
{
"model": "devstral:latest",
"stream": true,
"tools": [
{
"type": "function",
"function": {
"type": "function",
"name": "tool_get_current_time_post",
"description": "Get current time in a specific timezones",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
}
},
"required": [
"timezone"
]
}
}
},
{
"type": "function",
"function": {
"type": "function",
"name": "tool_convert_time_post",
"description": "Convert time between timezones",
"parameters": {
"type": "object",
"properties": {
"source_timezone": {
"type": "string",
"description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
},
"time": {
"type": "string",
"description": "Time to convert in 24-hour format (HH:MM)"
},
"target_timezone": {
"type": "string",
"description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
}
},
"required": [
"source_timezone",
"time",
"target_timezone"
]
}
}
}
],
"messages": [
{
"role": "user",
"content": "What time is it in Paris and New York?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "call_9edcccec-beb6-4bb3-a135-b898f9f87f6a",
"type": "function",
"function": {
"name": "tool_get_current_time_posttool_get_current_time_post",
"arguments": "{\"timezone\": \"Europe/Paris\"}{\"timezone\": \"America/New_York\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_9edcccec-beb6-4bb3-a135-b898f9f87f6a",
"content": null
}
]
}
</details>
Performing the request manually using `curl`:
<details>
$ curl -s http://localhost:11434/api/chat -d '{
"model": "devstral",
"messages": [
{
"role": "user",
"content": "What time is it in Paris and New York"
}
],
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "tool_get_current_time_post",
"description": "Get current time in a specific timezones",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
}
},
"required": ["timezone"]
}
}
},
{
"type": "function",
"function": {
"name": "tool_convert_time_post",
"description": "Convert time between timezones",
"parameters": {
"type": "object",
"properties": {
"source_timezone": {
"type": "string",
"description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
},
"time": {
"type": "string",
"description": "Time to convert in 24-hour format (HH:MM)"
},
"target_timezone": {
"type": "string",
"description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
}
},
"required": ["source_timezone", "time", "target_timezone"]
}
}
}
]
}' | jq
{
"model": "devstral",
"created_at": "2025-06-01T15:55:15.518588Z",
"message": {
"role": "assistant",
"content": "",
"tool_calls": [
{
"function": {
"name": "tool_get_current_time_post",
"arguments": {
"timezone": "Europe/Paris"
}
}
},
{
"function": {
"name": "tool_get_current_time_post",
"arguments": {
"timezone": "America/New_York"
}
}
}
]
},
"done_reason": "stop",
"done": true,
"total_duration": 6700734042,
"load_duration": 26193667,
"prompt_eval_count": 1491,
"prompt_eval_duration": 2590387959,
"eval_count": 45,
"eval_duration": 4075518458
}
</details>
Example of the same call with the same model running on an OpenAI-compatible backend (in this example, LM Studio):
<img width="1512" alt="OpenAI-compatible backend working multi-tool-calling example" src="https://github.com/user-attachments/assets/6f9d7477-0d0c-440c-9d5e-22a49ef24d01" />
Associated Open WebUI response log:
<details>
{
"model": "devstral",
"stream": true,
"tools": [
{
"type": "function",
"function": {
"type": "function",
"name": "tool_get_current_time_post",
"description": "Get current time in a specific timezones",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
}
},
"required": [
"timezone"
]
}
}
},
{
"type": "function",
"function": {
"type": "function",
"name": "tool_convert_time_post",
"description": "Convert time between timezones",
"parameters": {
"type": "object",
"properties": {
"source_timezone": {
"type": "string",
"description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
},
"time": {
"type": "string",
"description": "Time to convert in 24-hour format (HH:MM)"
},
"target_timezone": {
"type": "string",
"description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
}
},
"required": [
"source_timezone",
"time",
"target_timezone"
]
}
}
}
],
"messages": [
{
"role": "user",
"content": "What time is it in Paris and New-York?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"index": 0,
"id": "550202006",
"type": "function",
"function": {
"name": "tool_get_current_time_post",
"arguments": "{\"timezone\": \"Europe/Paris\"}"
}
},
{
"index": 1,
"id": "782814921",
"type": "function",
"function": {
"name": "tool_get_current_time_post",
"arguments": "{\"timezone\": \"America/New_York\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "550202006",
"content": "{\n \"timezone\": \"Europe/Paris\",\n \"datetime\": \"2025-06-01T18:04:59+02:00\",\n \"is_dst\": true\n}"
},
{
"role": "tool",
"tool_call_id": "782814921",
"content": "{\n \"timezone\": \"America/New_York\",\n \"datetime\": \"2025-06-01T12:04:59-04:00\",\n \"is_dst\": true\n}"
}
]
}
</details>
### Additional Information
I believe this behavior stems from the following code snippet:
https://github.com/open-webui/open-webui/blob/53764fe64884da147359e54ed6d9607fe57f1600/backend/open_webui/utils/middleware.py#L1811-L1821
I added debug logs and compared output with both the Ollama and LM Studio backends.
Ollama:
<details>
open_webui.utils.middleware:stream_body_handler:1787 - current_response_tool_call is None - {}
open_webui.utils.middleware:stream_body_handler:1788 - BEFORE delta_tool_call: {'index': 0, 'id': 'call_38a3474f-8838-4b9f-a22a-a506c9678c52', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}'}} - {}
open_webui.utils.middleware:stream_body_handler:1802 - AFTER delta_tool_call: {'index': 0, 'id': 'call_38a3474f-8838-4b9f-a22a-a506c9678c52', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}'}} - {}
open_webui.utils.middleware:stream_body_handler:1803 - response_tool_calls: [{'index': 0, 'id': 'call_38a3474f-8838-4b9f-a22a-a506c9678c52', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}'}}] - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': 'call_38a3474f-8838-4b9f-a22a-a506c9678c52', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': 'call_38a3474f-8838-4b9f-a22a-a506c9678c52', 'type': 'function', 'function': {'name': 'tool_get_current_time_posttool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}{"timezone": "America/New_York"}'}} - {}
</details>
LM Studio:
<details>
open_webui.utils.middleware:stream_body_handler:1787 - current_response_tool_call is None - {}
open_webui.utils.middleware:stream_body_handler:1788 - BEFORE delta_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}} - {}
open_webui.utils.middleware:stream_body_handler:1802 - AFTER delta_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}} - {}
open_webui.utils.middleware:stream_body_handler:1803 - response_tool_calls: [{'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}}] - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"time'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"time'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone":'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone":'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}'}} - {}
open_webui.utils.middleware:stream_body_handler:1787 - current_response_tool_call is None - {}
open_webui.utils.middleware:stream_body_handler:1788 - BEFORE delta_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}} - {}
open_webui.utils.middleware:stream_body_handler:1802 - AFTER delta_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}} - {}
open_webui.utils.middleware:stream_body_handler:1803 - response_tool_calls: [{'index': 0, 'id': '918251941', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "Europe/Paris"}'}}, {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}}] - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': ''}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"time'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"time'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone":'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone":'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_Y'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_Y'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_York'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_York'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_York"'}} - {}
open_webui.utils.middleware:stream_body_handler:1805 - current_response_tool_call is not None, updating tool call - {}
open_webui.utils.middleware:stream_body_handler:1806 - BEFORE current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_York"'}} - {}
open_webui.utils.middleware:stream_body_handler:1828 - AFTER current_response_tool_call: {'index': 1, 'id': '664445966', 'type': 'function', 'function': {'name': 'tool_get_current_time_post', 'arguments': '{"timezone": "America/New_York"}'}} - {}
</details>
However, I believe the bug itself is in this code instead:
https://github.com/open-webui/open-webui/blob/53764fe64884da147359e54ed6d9607fe57f1600/backend/open_webui/utils/response.py#L9-L24
From the manual `curl` request in pasted above, you can see that the Ollama `tool_calls` array does not contain an `index` parameter. As such, it always defaults to 0 in `convert_ollama_tool_call_to_openai`. Because there are multiple tools, they all end up sharing the same index, which is why they are later concatenated together by `stream_body_handler`.
I believe the fix would be to change the `convert_ollama_tool_call_to_openai` function so that it increments the default value for `index` and makes sure there are no collisions
GiteaMirror
added the bug label 2026-05-13 03:52:53 -05:00
Works fine for me. You're probably using the default 2048 max num_ctx length and its running out of tokens while deciding whether to use the tool, causing failure. Try increasing it to 10-15k tokens minimum. I can reproduce if I set it to 2048 and it works fine at 12k.
This doesn't happen with LM Studio because you're using it as a generic OpenAI API compatible endpoint and it's not passing Open WebUI's default num_ctx to the backend, and thus respecting whatever the configured baseline max context settings is for the model running. With Ollama, you are overriding whatever is set at the model level (in ollama show)
<!-- gh-comment-id:2927919190 -->
@taylorwilsdon commented on GitHub (Jun 1, 2025):
Works fine for me. You're probably using the default 2048 max num_ctx length and its running out of tokens while deciding whether to use the tool, causing failure. Try increasing it to 10-15k tokens minimum. I can reproduce if I set it to 2048 and it works fine at 12k.
This doesn't happen with LM Studio because you're using it as a generic OpenAI API compatible endpoint and it's not passing Open WebUI's default num_ctx to the backend, and thus respecting whatever the configured baseline max context settings is for the model running. With Ollama, you are overriding whatever is set at the model level (in `ollama show`)
<img width="1027" alt="Image" src="https://github.com/user-attachments/assets/1eaa0f08-f9b0-4a68-ab01-a9f541618c8c" />
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @JRomainG on GitHub (Jun 1, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/14583
Check Existing Issues
Installation Method
Docker
Open WebUI Version
v0.6.13
Ollama Version (if applicable)
0.9.0
Operating System
macOS Sequoia (15.5)
Browser (if applicable)
Firefox 139.0.1
Confirmation
README.md.Expected Behavior
When the Ollama API returns multiple tools to run, Open WebUI calls each tool one by one, like for the OpenAI-compatible backends
Actual Behavior
The Open WebUI middleware concatenates all tool calls together when using the Ollama backend, resulting in a single invalid tool. In addition, because the tool does not exist, the Open WebUI frontend loads indefinitely without ever performing a request to the tool server (which might be related to #14577)
Steps to Reproduce
devstral:latest)Logs & Screenshots
Example of invalid tool call using the Ollama backend:

Associated Open WebUI response log:
Performing the request manually using
curl:{
"model": "devstral",
"created_at": "2025-06-01T15:55:15.518588Z",
"message": {
"role": "assistant",
"content": "",
"tool_calls": [
{
"function": {
"name": "tool_get_current_time_post",
"arguments": {
"timezone": "Europe/Paris"
}
}
},
{
"function": {
"name": "tool_get_current_time_post",
"arguments": {
"timezone": "America/New_York"
}
}
}
]
},
"done_reason": "stop",
"done": true,
"total_duration": 6700734042,
"load_duration": 26193667,
"prompt_eval_count": 1491,
"prompt_eval_duration": 2590387959,
"eval_count": 45,
"eval_duration": 4075518458
}
Example of the same call with the same model running on an OpenAI-compatible backend (in this example, LM Studio):

Associated Open WebUI response log:
Additional Information
I believe this behavior stems from the following code snippet:
https://github.com/open-webui/open-webui/blob/53764fe64884da147359e54ed6d9607fe57f1600/backend/open_webui/utils/middleware.py#L1811-L1821
I added debug logs and compared output with both the Ollama and LM Studio backends.
Ollama:
LM Studio:
However, I believe the bug itself is in this code instead:
https://github.com/open-webui/open-webui/blob/53764fe64884da147359e54ed6d9607fe57f1600/backend/open_webui/utils/response.py#L9-L24
From the manual
curlrequest in pasted above, you can see that the Ollamatool_callsarray does not contain anindexparameter. As such, it always defaults to 0 inconvert_ollama_tool_call_to_openai. Because there are multiple tools, they all end up sharing the same index, which is why they are later concatenated together bystream_body_handler.I believe the fix would be to change the
convert_ollama_tool_call_to_openaifunction so that it increments the default value forindexand makes sure there are no collisions@taylorwilsdon commented on GitHub (Jun 1, 2025):
Works fine for me. You're probably using the default 2048 max num_ctx length and its running out of tokens while deciding whether to use the tool, causing failure. Try increasing it to 10-15k tokens minimum. I can reproduce if I set it to 2048 and it works fine at 12k.
This doesn't happen with LM Studio because you're using it as a generic OpenAI API compatible endpoint and it's not passing Open WebUI's default num_ctx to the backend, and thus respecting whatever the configured baseline max context settings is for the model running. With Ollama, you are overriding whatever is set at the model level (in
ollama show)