Skip to content

fix: chat corruption (openweb-ui#26668)#26971

Open
intdel wants to merge 1 commit into
open-webui:devfrom
intdel:fix_corruption
Open

fix: chat corruption (openweb-ui#26668)#26971
intdel wants to merge 1 commit into
open-webui:devfrom
intdel:fix_corruption

Conversation

@intdel

@intdel intdel commented Jul 10, 2026

Copy link
Copy Markdown

Pull Request Checklist

Note to first-time contributors: Please open a discussion post in Discussions to discuss your idea/fix with the community before creating a pull request, and describe your changes before submitting a pull request.

This is to ensure large feature PRs are discussed with the community first, before starting work on it. If the community does not want this feature or it is not relevant for Open WebUI as a project, it can be identified in the discussion before working on the feature and submitting the PR.

Before submitting, make sure you've checked the following:

  • Linked Issue/Discussion: This PR references an existing Issue or DiscussionCloses #___ / Relates to #___. If one does not exist, create one first. PRs without a linked issue or discussion may be closed without review.
  • Target branch: The pull request targets the dev branch. PRs targeting main will be immediately closed.
  • Description: A concise description of the changes is provided below.
  • Changelog: A changelog entry following Keep a Changelog format is included at the bottom.
  • Documentation: Relevant documentation has been added or updated in the Open WebUI Docs Repository.
  • Dependencies: Any new or updated dependencies are explained, tested, and documented.
  • Testing: Manual tests have been performed to verify the fix/feature works correctly and does not introduce regressions. Screenshots or recordings are included where applicable.
  • No Unchecked AI Code: This PR is either human-written or has undergone thorough human review AND manual testing. Unreviewed AI-generated PRs may be closed immediately.
  • Self-Review: A self-review of the code has been performed, ensuring adherence to project coding standards.
  • Architecture: Smart defaults are preferred over new settings. Local state is used for ephemeral UI logic. Major architectural or UX changes have been discussed first.
  • Git Hygiene: The PR is atomic (one logical change), rebased on dev, and contains no unrelated commits.
  • Title Prefix: The PR title uses one of the following prefixes:
    • BREAKING CHANGE: Changes affecting backward compatibility
    • build: Build system or dependency changes
    • ci: CI/CD workflow changes
    • chore: Refactoring, cleanup, or non-functional changes
    • docs: Documentation additions or updates
    • feat: New features or enhancements
    • fix: Bug fixes or corrections
    • i18n: Internationalization or localization changes
    • perf: Performance improvements
    • refactor: Code restructuring
    • style: Formatting changes (whitespace, semicolons, etc.)
    • test: Test additions or corrections
    • WIP: Work in progress

Changelog Entry

Description

Deleting a user message while its reply was still streaming corrupted the chat: after the generation finished, only the streamed reply survived on reload and the rest of the history became unreachable.

Root cause: deleting a message did not cancel the in-flight generation. The backend task kept running and, on completion, upserted the deleted assistant message with no resolvable parent — recreating it as an orphan root and pointing currentId at it. createMessagesList walks parentId from currentId, so the broken parent chain stranded the prior conversation.

Three fixes:

  1. Frontend (Messages.svelte): when the message being deleted (or one of its reply children) is the not-yet-done assistant reply at currentId, call stopResponse(false) first to cancel the backend task. Gate on the message done-state (the codebase's own isGenerating heuristic) rather than the generating flag, which is only set in the MoA merge path. stopResponse is threaded through from Chat.svelte as a prop.

  2. Backend (chats.py upsert_message_to_chat_by_id_and_message_id): if a stale completion upsert arrives for a message that is no longer in the chat with no resolvable parent (parent deleted, or no surviving message references it in childrenIds), skip the write instead of resurrecting it as an orphan root. Normal flow is unaffected — the user message is persisted before generation, so the assistant upsert resolves a live parent or hits the existing-message merge branch.

  3. Backend: only advance currentId to the upserted message when its parentId is None or exists in the messages map, so currentId can never point at a message with a broken parent chain.

Fixed


Additional Information

  • I used AI to fix it, but I reviewed the code and tested it manually

Screenshots or Videos

Screen.Recording.2026-07-10.102926.mp4

Contributor License Agreement

Note

Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.

open-webui#26668)

Deleting a user message while its reply was still streaming corrupted the
chat: after the generation finished, only the streamed reply survived on
reload and the rest of the history became unreachable.

Root cause: deleting a message did not cancel the in-flight generation.
The backend task kept running and, on completion, upserted the deleted
assistant message with no resolvable parent — recreating it as an orphan
root and pointing currentId at it. createMessagesList walks parentId from
currentId, so the broken parent chain stranded the prior conversation.

Three fixes:

1. Frontend (Messages.svelte): when the message being deleted (or one of
   its reply children) is the not-yet-done assistant reply at currentId,
   call stopResponse(false) first to cancel the backend task. Gate on the
   message done-state (the codebase's own isGenerating heuristic) rather
   than the `generating` flag, which is only set in the MoA merge path.
   stopResponse is threaded through from Chat.svelte as a prop.

2. Backend (chats.py upsert_message_to_chat_by_id_and_message_id): if a
   stale completion upsert arrives for a message that is no longer in the
   chat with no resolvable parent (parent deleted, or no surviving message
   references it in childrenIds), skip the write instead of resurrecting
   it as an orphan root. Normal flow is unaffected — the user message is
   persisted before generation, so the assistant upsert resolves a live
   parent or hits the existing-message merge branch.

3. Backend: only advance currentId to the upserted message when its
   parentId is None or exists in the messages map, so currentId can never
   point at a message with a broken parent chain.
@intdel intdel changed the title fix(chat): chat corruption (openweb-ui#26668) fix: chat corruption (openweb-ui#26668) Jul 10, 2026
@Classic298

Copy link
Copy Markdown
Collaborator

thanks for the video!

@silentoplayz

Copy link
Copy Markdown
Collaborator

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants