[GH-ISSUE #24758] issue: convert_output_to_messages produces orphan tool_calls when function_call_output is missing from stored output #123708

Closed
opened 2026-05-21 03:10:11 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @marcodigitalhub on GitHub (May 15, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/24758

Check Existing Issues

  • I have searched for any existing and/or related issues.
  • I have searched for any existing and/or related discussions.
  • I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
  • I am using the latest version of Open WebUI.

Installation Method

Git Clone

Open WebUI Version

0.9.5

Ollama Version (if applicable)

No response

Operating System

macOS and Ubuntu 23.04

Browser (if applicable)

No response

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 resuming a chat that previously triggered tool calls, the conversation should continue normally even if some tool results were never stored in the output field (for example, because the knowledge base was updated between turns).

Actual Behavior

The resumed chat fails with a 400 error from the LLM provider. The conversation history sent to the model contains assistant messages with tool_calls that have no matching tool role messages after them. Providers that enforce strict pairing (AWS Bedrock Converse, Anthropic) reject this with:

ValidationException: messages.N: tool_use ids were found without tool_result blocks
immediately after: tooluse_3qcFqdKrabOz1nRCiGud8e, tooluse_7ajZ3VeDkelObImRYCcjKg.
Each tool_use block must have a corresponding tool_result block in the next message.

Steps to Reproduce

Deploy Open WebUI connected to AWS Bedrock via the bedrock-access-gateway (or any strict provider that enforces tool_use/tool_result pairing).

Enable native function calling (function_calling: native in model params) and attach at least one tool to the model (a knowledge base lookup tool works well for this).

Start a new chat and send a message that triggers the tool. Wait for the assistant to respond using the tool. At this point the DB stores the assistant's output field with function_call and function_call_output items.

Now update the knowledge base (add or remove a file). This causes the tool's internal state to change.

In the same chat, send a follow-up message. Open WebUI loads the message history from the DB via load_messages_from_db, passes it through process_messages_with_output, which calls convert_output_to_messages.

The function fails with the 400 ValidationException shown above.

The root cause is in misc.py in convert_output_to_messages. The function collects function_call items into pending_tool_calls and only flushes them into an assistant message when it encounters a function_call_output. If a function_call_output is missing from the stored output (because it was never written, or was lost during the KB update), the final flush_pending() at line 280 emits an assistant message with tool_calls but no subsequent tool role message follows it.

You can also reproduce this programmatically without the UI by saving a chat with orphan function_call items in the output field and then sending a completion request with chat_id set to that chat's ID.

Logs & Screenshots

Error returned by the API:

{
  "detail": "An error occurred (ValidationException) when calling the Converse operation: The model returned the following errors: messages.2: `tool_use` ids were found without `tool_result` blocks immediately after: tooluse_3qcFqdKrabOz1nRCiGud8e, tooluse_7ajZ3VeDkelObImRYCcjKg. Each `tool_use` block must have a corresponding `tool_result` block in the next message."
}

Additional Information

The fix is straightforward. After flush_pending() at the end of convert_output_to_messages, scan the produced message list and strip any tool_calls whose id has no matching tool_call_id in a subsequent tool role message. If stripping leaves an assistant message with empty content and no tool_calls, drop it entirely. Well-formed sequences are unaffected since the check is a no-op when all ids match.

I have a working patch, tested in local and AWS setups, and can open a PR targeting dev if this approach sounds reasonable.

Originally created by @marcodigitalhub on GitHub (May 15, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/24758 ### Check Existing Issues - [x] I have searched for any existing and/or related issues. - [x] I have searched for any existing and/or related discussions. - [x] I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!). - [x] I am using the latest version of Open WebUI. ### Installation Method Git Clone ### Open WebUI Version 0.9.5 ### Ollama Version (if applicable) _No response_ ### Operating System macOS and Ubuntu 23.04 ### Browser (if applicable) _No response_ ### 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 resuming a chat that previously triggered tool calls, the conversation should continue normally even if some tool results were never stored in the output field (for example, because the knowledge base was updated between turns). ### Actual Behavior The resumed chat fails with a 400 error from the LLM provider. The conversation history sent to the model contains assistant messages with tool_calls that have no matching tool role messages after them. Providers that enforce strict pairing (AWS Bedrock Converse, Anthropic) reject this with: ``` ValidationException: messages.N: tool_use ids were found without tool_result blocks immediately after: tooluse_3qcFqdKrabOz1nRCiGud8e, tooluse_7ajZ3VeDkelObImRYCcjKg. Each tool_use block must have a corresponding tool_result block in the next message. ``` ### Steps to Reproduce Deploy Open WebUI connected to AWS Bedrock via the bedrock-access-gateway (or any strict provider that enforces tool_use/tool_result pairing). Enable native function calling (`function_calling: native` in model params) and attach at least one tool to the model (a knowledge base lookup tool works well for this). Start a new chat and send a message that triggers the tool. Wait for the assistant to respond using the tool. At this point the DB stores the assistant's `output` field with `function_call` and `function_call_output` items. Now update the knowledge base (add or remove a file). This causes the tool's internal state to change. In the same chat, send a follow-up message. Open WebUI loads the message history from the DB via `load_messages_from_db`, passes it through `process_messages_with_output`, which calls `convert_output_to_messages`. The function fails with the 400 ValidationException shown above. The root cause is in `misc.py` in `convert_output_to_messages`. The function collects function_call items into `pending_tool_calls` and only flushes them into an assistant message when it encounters a `function_call_output`. If a `function_call_output` is missing from the stored output (because it was never written, or was lost during the KB update), the final `flush_pending()` at line 280 emits an assistant message with `tool_calls` but no subsequent tool role message follows it. You can also reproduce this programmatically without the UI by saving a chat with orphan function_call items in the output field and then sending a completion request with `chat_id` set to that chat's ID. ### Logs & Screenshots Error returned by the API: ``` { "detail": "An error occurred (ValidationException) when calling the Converse operation: The model returned the following errors: messages.2: `tool_use` ids were found without `tool_result` blocks immediately after: tooluse_3qcFqdKrabOz1nRCiGud8e, tooluse_7ajZ3VeDkelObImRYCcjKg. Each `tool_use` block must have a corresponding `tool_result` block in the next message." } ``` ### Additional Information The fix is straightforward. After `flush_pending()` at the end of `convert_output_to_messages`, scan the produced message list and strip any `tool_calls` whose id has no matching `tool_call_id` in a subsequent tool role message. If stripping leaves an assistant message with empty content and no `tool_calls`, drop it entirely. Well-formed sequences are unaffected since the check is a no-op when all ids match. I have a working patch, tested in local and AWS setups, and can open a PR targeting dev if this approach sounds reasonable.
GiteaMirror added the bug label 2026-05-21 03:10:11 -05:00
Author
Owner

@owui-terminator[bot] commented on GitHub (May 15, 2026):

🔍 Related Issues Found

I found some existing issues that might be related. Please check if any of these are duplicates or contain helpful solutions:

  1. 🟣 #21471 issue: v0.8.2 Unexpected tool_use_id
    Reports a tool-use pairing problem after native tool calling that blocks follow-up messages in Claude/Litellm-style providers. While the exact cause differs, it is closely related to malformed tool call history causing provider validation errors.
    by mustard123 · bug

  2. 🟣 #12214 issue: Tool Calls result overrides previous tools messages sent
    Describes native tool calling producing incorrect/overwritten tool message handling in the chat history. This is related to the same general area of assistant/tool message sequencing and message reconstruction.
    by ivanwong1989 · bug

  3. 🟣 #14157 issue: Switching models in chat does not deselect previously selected tools
    About tool selection/state persisting incorrectly across model switches, which is adjacent to the chat/tool state handling involved here. It is less direct, but still touches the same tool-context lifecycle.
    by jrkropp · bug, help wanted, testing wanted


💡 If your issue is a duplicate, please close it and add any additional details to the existing issue instead.

This comment was generated automatically. React with 👍 if helpful, 👎 if not.

<!-- gh-comment-id:4459769989 --> @owui-terminator[bot] commented on GitHub (May 15, 2026): <!-- terminator-bot:related-issues-reply --> 🔍 **Related Issues Found** I found some existing issues that might be related. Please check if any of these are duplicates or contain helpful solutions: 1. 🟣 [#21471](https://github.com/open-webui/open-webui/issues/21471) **issue: v0.8.2 Unexpected tool_use_id** *Reports a tool-use pairing problem after native tool calling that blocks follow-up messages in Claude/Litellm-style providers. While the exact cause differs, it is closely related to malformed tool call history causing provider validation errors.* *by mustard123 · `bug`* 2. 🟣 [#12214](https://github.com/open-webui/open-webui/issues/12214) **issue: Tool Calls result overrides previous tools messages sent** *Describes native tool calling producing incorrect/overwritten tool message handling in the chat history. This is related to the same general area of assistant/tool message sequencing and message reconstruction.* *by ivanwong1989 · `bug`* 3. 🟣 [#14157](https://github.com/open-webui/open-webui/issues/14157) **issue: Switching models in chat does not deselect previously selected tools** *About tool selection/state persisting incorrectly across model switches, which is adjacent to the chat/tool state handling involved here. It is less direct, but still touches the same tool-context lifecycle.* *by jrkropp · `bug`, `help wanted`, `testing wanted`* --- 💡 If your issue is a duplicate, please close it and add any additional details to the existing issue instead. *This comment was generated automatically.* React with 👍 if helpful, 👎 if not.
Author
Owner

@Classic298 commented on GitHub (May 15, 2026):

Why is function call output missing?

That's the actual question here

Do you have any custom filters or pipes?

<!-- gh-comment-id:4459824989 --> @Classic298 commented on GitHub (May 15, 2026): Why is function call output missing? That's the actual question here Do you have any custom filters or pipes?
Author
Owner

@marcodigitalhub commented on GitHub (May 15, 2026):

@Classic298 Why is function_call_output missing?:

From what we've traced, it happens when the user updates the knowledge base and then continues an existing chat. The frontend rewrites the message history on KB changes and the rewrite doesn't always preserve the full output structure. The function_call items survive but their matching function_call_output entries get lost.

We've also seen it when a tool call is interrupted (user cancels or connection drops mid-execution). The function_call gets written first as a streaming event, but the result never arrives. Either way, convert_output_to_messages should handle incomplete output gracefully. An orphan tool_calls with no matching result is never valid for any provider, so stripping it at reconstruction time seems like the right safety net.

Do you have any custom filters or pipes?

Just one global filter for content safety (Bedrock Guardrails). It only inspects user input and model output text, doesn't touch message history or tool_calls at all. No custom pipes. We use MCP tool servers for tools.

The issue reproduces with the filter disabled too. The root cause is entirely in how convert_output_to_messages handles incomplete output arrays.

<!-- gh-comment-id:4460140358 --> @marcodigitalhub commented on GitHub (May 15, 2026): @Classic298 **Why is function_call_output missing?**: From what we've traced, it happens when the user updates the knowledge base and then continues an existing chat. The frontend rewrites the message history on KB changes and the rewrite doesn't always preserve the full output structure. The function_call items survive but their matching `function_call_output` entries get lost. We've also seen it when a tool call is interrupted (user cancels or connection drops mid-execution). The `function_call` gets written first as a streaming event, but the result never arrives. Either way, `convert_output_to_messages` should handle incomplete output gracefully. An orphan `tool_calls` with no matching result is never valid for any provider, so stripping it at reconstruction time seems like the right safety net. **Do you have any custom filters or pipes?** Just one global filter for content safety (Bedrock Guardrails). It only inspects user input and model output text, doesn't touch message history or `tool_calls` at all. No custom pipes. We use MCP tool servers for tools. The issue reproduces with the filter disabled too. The root cause is entirely in how `convert_output_to_messages` handles incomplete output arrays.
Author
Owner

@Classic298 commented on GitHub (May 15, 2026):

investigating

<!-- gh-comment-id:4463177444 --> @Classic298 commented on GitHub (May 15, 2026): investigating
Author
Owner

@Classic298 commented on GitHub (May 15, 2026):

Related but not direct fix for your report: https://github.com/open-webui/open-webui/pull/24617 https://github.com/open-webui/open-webui/pull/24799

<!-- gh-comment-id:4463286836 --> @Classic298 commented on GitHub (May 15, 2026): Related but not direct fix for your report: https://github.com/open-webui/open-webui/pull/24617 https://github.com/open-webui/open-webui/pull/24799
Author
Owner

@Classic298 commented on GitHub (May 15, 2026):

@marcodigitalhub testing wanted https://github.com/open-webui/open-webui/pull/24798

<!-- gh-comment-id:4463518402 --> @Classic298 commented on GitHub (May 15, 2026): @marcodigitalhub testing wanted https://github.com/open-webui/open-webui/pull/24798
Author
Owner

@marcodigitalhub commented on GitHub (May 16, 2026):

@Classic298 Tested locally against four scenarios. Without the fix, three of four fail, orphan tool_call survives, all-orphan turns leak tool_use ids with no tool_result, and orphan calls beside text content still produce unbalanced output. With the fix, all four produce a balanced sequence and the well-formed case is unchanged.

LGTM. One suggestion: a unit test in the repo would lock this in against future regressions. Happy to open a follow-up PR with the four cases above as pytest parametrized tests if you want.

<!-- gh-comment-id:4466077169 --> @marcodigitalhub commented on GitHub (May 16, 2026): @Classic298 Tested locally against four scenarios. Without the fix, three of four fail, orphan `tool_call` survives, all-orphan turns leak `tool_use` ids with no `tool_result`, and orphan calls beside text content still produce unbalanced output. With the fix, all four produce a balanced sequence and the well-formed case is unchanged. LGTM. One suggestion: a unit test in the repo would lock this in against future regressions. Happy to open a follow-up PR with the four cases above as `pytest` parametrized tests if you want.
Author
Owner

@Classic298 commented on GitHub (May 16, 2026):

@marcodigitalhub thanks we have an internal unit test repo i already added it there

<!-- gh-comment-id:4466329274 --> @Classic298 commented on GitHub (May 16, 2026): @marcodigitalhub thanks we have an internal unit test repo i already added it there
Author
Owner

@Classic298 commented on GitHub (May 19, 2026):

should be fixed in dev testing wanted

<!-- gh-comment-id:4490880578 --> @Classic298 commented on GitHub (May 19, 2026): should be fixed in dev testing wanted
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#123708