[PR #24834] [CLOSED] fix: strip orphan tool_calls in convert_output_to_messages #115191

Closed
opened 2026-05-18 16:08:37 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/24834
Author: @kindredzhang
Created: 5/17/2026
Status: ❌ Closed

Base: main ← Head: fix/24758-orphan-tool-calls


📝 Commits (10+)

📊 Changes

297 files changed (+16375 additions, -101553 deletions)

View changed files

📝 .github/ISSUE_TEMPLATE/feature_request.yaml (+34 -40)
📝 .github/dependabot.yml (+10 -5)
📝 .github/pull_request_template.md (+33 -34)
➕ .github/workflows/backend.yaml (+40 -0)
➖ .github/workflows/build-release.yml (+0 -61)
➖ .github/workflows/docker-build.yaml (+0 -917)
➕ .github/workflows/docker.yaml (+311 -0)
➖ .github/workflows/format-backend.yaml (+0 -46)
➖ .github/workflows/format-build-frontend.yaml (+0 -65)
➕ .github/workflows/frontend.yaml (+63 -0)
➖ .github/workflows/integration-test.disabled (+0 -255)
➕ .github/workflows/release.yml (+69 -0)
📝 backend/open_webui/config.py (+3001 -3236)
📝 backend/open_webui/constants.py (+5 -3)
📝 backend/open_webui/env.py (+408 -472)
📝 backend/open_webui/functions.py (+15 -21)
➕ backend/open_webui/internal/config.py (+258 -0)
📝 backend/open_webui/internal/db.py (+19 -54)
➖ backend/open_webui/internal/migrations/001_initial_schema.py (+0 -253)
➖ backend/open_webui/internal/migrations/002_add_local_sharing.py (+0 -45)

...and 80 more files

📄 Description

Pull Request Checklist

  • Linked Issue/Discussion: Closes #24758
  • Target branch: dev
  • Description: Concise description down below.
  • Changelog: Added at the bottom.
  • Documentation: N/A — backend logic fix, no new user-facing API or config.
  • Dependencies: N/A — no new dependencies.
  • Testing: Logic validated by code review; issue root cause confirmed.
  • Agentic AI Code: Human reviewed and manually verified.
  • Code review: Performed self-review.
  • Design & Architecture: Minimal, targeted fix; no new settings or state introduced.
  • Git Hygiene: Single atomic commit.
  • Title Prefix: fix

Description

When resuming a chat that previously triggered tool calls, the conversation can fail with a 400 error from strict LLM providers (AWS Bedrock, Anthropic). The root cause is in convert_output_to_messages() in misc.py:

output[] traversal:
  'function_call'     → accumulates in pending_tool_calls
  'function_call_output' → flush_pending() → appends tool role message,
                              clears pending_tool_calls
  ...
  final flush_pending() → if no function_call_output was ever encountered,
                           emits an assistant message with tool_calls but no
                           trailing tool role message

Strict providers enforce that every tool_use id has a matching tool_result block immediately after. If function_call_output is missing from stored history (e.g., KB state changed between turns), the orphaned tool_calls cause:

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

Fix

After flush_pending() at the end of convert_output_to_messages, scan the produced messages and collect all tool_call_id values from tool role messages. Then walk the messages again and strip any tool_calls whose id has no match. 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.

Root Cause

convert_output_to_messages assumes every function_call will be followed by a function_call_output before the function returns. When that assumption is violated (DB never wrote it, KB update cleared it, etc.), the final flush_pending() emits a malformed assistant message.

Validation

ruff format --check backend/open_webui/utils/misc.py
ruff check backend/open_webui/utils/misc.py

Changelog Entry

Fixed

  • convert_output_to_messages now strips orphan tool_calls that lack a matching tool role message, preventing 400 errors from strict providers (AWS Bedrock, Anthropic) when chat history is resumed with missing function_call_output entries.

Screenshots or Videos

Not applicable; backend logic fix, no UI change.


Contributor License Agreement


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/24834 **Author:** [@kindredzhang](https://github.com/kindredzhang) **Created:** 5/17/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/24758-orphan-tool-calls` --- ### 📝 Commits (10+) - [`18ff358`](https://github.com/open-webui/open-webui/commit/18ff358c15f4fa7986be48e2282ba437342b48ea) refac - [`90a02e6`](https://github.com/open-webui/open-webui/commit/90a02e623f05027dec3ae28a394b256497b9d70d) refac - [`070ab26`](https://github.com/open-webui/open-webui/commit/070ab2650128b6af03d72be29f90f78cbf3584bc) refac: reorganize scripts and ci workflows - [`9c18e70`](https://github.com/open-webui/open-webui/commit/9c18e707ce764ed64655d7681f5b5be0f466885d) chore: remove unused cypress tests - [`4527c74`](https://github.com/open-webui/open-webui/commit/4527c747b42d823b363f357c565c8bcafcf45cb7) chore: remove unused legacy test suite - [`2c2d06c`](https://github.com/open-webui/open-webui/commit/2c2d06c31b463a87d4ece2a4eb4c1d2f73cd3980) refac: deprecate peewee migration layer - [`c79ff81`](https://github.com/open-webui/open-webui/commit/c79ff81f551f37391e27a6219c25112dccb0f68b) refac: remove dead peewee connection wrappers - [`1413ce4`](https://github.com/open-webui/open-webui/commit/1413ce4a5260f600b0602e730212a487b680cd82) refac - [`4cee7c2`](https://github.com/open-webui/open-webui/commit/4cee7c29a5909e87ab420c459680084ed353f9e7) refac - [`83736e8`](https://github.com/open-webui/open-webui/commit/83736e8dbd9956b67108ca6983405bb2d17756e5) style: ruff format ### 📊 Changes **297 files changed** (+16375 additions, -101553 deletions) <details> <summary>View changed files</summary> 📝 `.github/ISSUE_TEMPLATE/feature_request.yaml` (+34 -40) 📝 `.github/dependabot.yml` (+10 -5) 📝 `.github/pull_request_template.md` (+33 -34) ➕ `.github/workflows/backend.yaml` (+40 -0) ➖ `.github/workflows/build-release.yml` (+0 -61) ➖ `.github/workflows/docker-build.yaml` (+0 -917) ➕ `.github/workflows/docker.yaml` (+311 -0) ➖ `.github/workflows/format-backend.yaml` (+0 -46) ➖ `.github/workflows/format-build-frontend.yaml` (+0 -65) ➕ `.github/workflows/frontend.yaml` (+63 -0) ➖ `.github/workflows/integration-test.disabled` (+0 -255) ➕ `.github/workflows/release.yml` (+69 -0) 📝 `backend/open_webui/config.py` (+3001 -3236) 📝 `backend/open_webui/constants.py` (+5 -3) 📝 `backend/open_webui/env.py` (+408 -472) 📝 `backend/open_webui/functions.py` (+15 -21) ➕ `backend/open_webui/internal/config.py` (+258 -0) 📝 `backend/open_webui/internal/db.py` (+19 -54) ➖ `backend/open_webui/internal/migrations/001_initial_schema.py` (+0 -253) ➖ `backend/open_webui/internal/migrations/002_add_local_sharing.py` (+0 -45) _...and 80 more files_ </details> ### 📄 Description # Pull Request Checklist - [x] **Linked Issue/Discussion:** `Closes #24758` - [x] **Target branch:** `dev` - [x] **Description:** Concise description down below. - [x] **Changelog:** Added at the bottom. - [x] **Documentation:** N/A — backend logic fix, no new user-facing API or config. - [x] **Dependencies:** N/A — no new dependencies. - [x] **Testing:** Logic validated by code review; issue root cause confirmed. - [x] **Agentic AI Code:** Human reviewed and manually verified. - [x] **Code review:** Performed self-review. - [x] **Design & Architecture:** Minimal, targeted fix; no new settings or state introduced. - [x] **Git Hygiene:** Single atomic commit. - [x] **Title Prefix:** `fix` --- ## Description When resuming a chat that previously triggered tool calls, the conversation can fail with a 400 error from strict LLM providers (AWS Bedrock, Anthropic). The root cause is in `convert_output_to_messages()` in `misc.py`: ``` output[] traversal: 'function_call' → accumulates in pending_tool_calls 'function_call_output' → flush_pending() → appends tool role message, clears pending_tool_calls ... final flush_pending() → if no function_call_output was ever encountered, emits an assistant message with tool_calls but no trailing tool role message ``` Strict providers enforce that every `tool_use` id has a matching `tool_result` block immediately after. If `function_call_output` is missing from stored history (e.g., KB state changed between turns), the orphaned `tool_calls` cause: ``` ValidationException: messages.N: tool_use ids were found without tool_result blocks immediately after: tooluse_xxx, tooluse_yyy. Each tool_use block must have a corresponding tool_result block in the next message. ``` ### Fix After `flush_pending()` at the end of `convert_output_to_messages`, scan the produced messages and collect all `tool_call_id` values from tool role messages. Then walk the messages again and strip any `tool_calls` whose id has no match. 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. ### Root Cause `convert_output_to_messages` assumes every `function_call` will be followed by a `function_call_output` before the function returns. When that assumption is violated (DB never wrote it, KB update cleared it, etc.), the final `flush_pending()` emits a malformed assistant message. ### Validation ```bash ruff format --check backend/open_webui/utils/misc.py ruff check backend/open_webui/utils/misc.py ``` --- ## Changelog Entry ### Fixed - `convert_output_to_messages` now strips orphan `tool_calls` that lack a matching `tool` role message, preventing 400 errors from strict providers (AWS Bedrock, Anthropic) when chat history is resumed with missing `function_call_output` entries. --- ## Screenshots or Videos Not applicable; backend logic fix, no UI change. --- ## Contributor License Agreement - [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-18 16:08:37 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#115191