[GH-ISSUE #23787] issue: file content update can silently lose KB embeddings when reindex fails #107067

Open
opened 2026-05-18 05:36:31 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @shaun0927 on GitHub (Apr 16, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/23787

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 exact duplicate.
  • I am using the latest version of Open WebUI.

Installation Method

Git Clone

Open WebUI Version

latest main as of 2026-04-16 (latest release also checked: v0.8.12)

Ollama Version (if applicable)

No response

Operating System

macOS Sequoia

Browser (if applicable)

No response

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of Open WebUI.
  • I have provided a deterministic local reproduction of the affected code path.
  • I have included the relevant code path, expected behavior, actual behavior, and exact reproduction steps.

Expected Behavior

Updating a file's content should not leave any knowledge collection in a worse state than before.

If the KB reindex step fails after the file content has been updated, Open WebUI should either:

  • preserve the old KB vectors, or
  • fail the request before destructive cleanup happens.

Actual Behavior

/files/{id}/data/content/update currently deletes the old KB vectors first and only then tries to rebuild them.

If the rebuild fails, the exception is reduced to a warning and the route still returns success. That can leave the knowledge collection without any vectors for that file.

This is different from #20558:

  • #20558 = stale old embeddings remained after edits
  • this report = the old embeddings are deleted, the rebuild fails, and the route still succeeds

Steps to Reproduce

The current main code in backend/open_webui/routers/files.py does this during content updates:

  1. update the file content via process_file(..., content=...)
  2. for each knowledge collection referencing the file:
    • delete(... filter={'file_id': id})
    • process_file(..., collection_name=knowledge.id)
  3. if step 2b fails, only log a warning and continue

A deterministic local reproduction is:

from dataclasses import dataclass

kb_vectors = {'kb-1': [{'file_id': 'file-1', 'chunk': 'OLD EMBEDDING'}]}

@dataclass
class Knowledge:
    id: str

class VECTOR_DB_CLIENT:
    @staticmethod
    def delete(collection_name, filter):
        kb_vectors[collection_name] = [
            v for v in kb_vectors.get(collection_name, [])
            if v.get('file_id') != filter['file_id']
        ]

class ProcessFileForm:
    def __init__(self, file_id, collection_name=None):
        self.file_id = file_id
        self.collection_name = collection_name

def process_file(request, form, user, db=None):
    if form.collection_name:
        raise RuntimeError('reindex failed')

for knowledge in [Knowledge(id='kb-1')]:
    try:
        VECTOR_DB_CLIENT.delete(collection_name=knowledge.id, filter={'file_id': 'file-1'})
        process_file(None, ProcessFileForm(file_id='file-1', collection_name=knowledge.id), user=None)
    except Exception as e:
        print('swallowed knowledge reindex failure:', e)

print('remaining_vectors =', kb_vectors['kb-1'])

Actual output:

swallowed knowledge reindex failure: reindex failed
remaining_vectors = []

The route-level call sequence is effectively:

process_file(file-1, content='NEW')
delete(kb-1, {'file_id': 'file-1'})
process_file(file-1, collection_name='kb-1')  # fails
return {'content': 'NEW'}

Logs & Screenshots

Relevant current code path (backend/open_webui/routers/files.py):

await ASYNC_VECTOR_DB_CLIENT.delete(collection_name=knowledge.id, filter={'file_id': id})
await process_file(
    request,
    ProcessFileForm(file_id=id, collection_name=knowledge.id),
    user=user,
    db=db,
)

and on failure:

log.warning(f'Failed to update knowledge {knowledge.id} after content change for file {id}: {e}')

Additional Information

Related but not exact duplicates:

  • #20558 fixed the stale-embedding case
  • #6311 is about failed embeddings handling in general

I have a narrow fix ready that rebuilds first and only deletes the stale vector ids after the new insert succeeds, plus a focused regression test.

Originally created by @shaun0927 on GitHub (Apr 16, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/23787 ### 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 exact duplicate. - [x] I am using the latest version of Open WebUI. ### Installation Method Git Clone ### Open WebUI Version latest `main` as of 2026-04-16 (latest release also checked: `v0.8.12`) ### Ollama Version (if applicable) _No response_ ### Operating System macOS Sequoia ### 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 Open WebUI. - [x] I have provided a deterministic local reproduction of the affected code path. - [x] I have included the relevant code path, expected behavior, actual behavior, and exact reproduction steps. ### Expected Behavior Updating a file's content should not leave any knowledge collection in a worse state than before. If the KB reindex step fails after the file content has been updated, Open WebUI should either: - preserve the old KB vectors, or - fail the request before destructive cleanup happens. ### Actual Behavior `/files/{id}/data/content/update` currently deletes the old KB vectors first and only then tries to rebuild them. If the rebuild fails, the exception is reduced to a warning and the route still returns success. That can leave the knowledge collection without any vectors for that file. This is different from `#20558`: - `#20558` = stale old embeddings remained after edits - this report = the old embeddings are deleted, the rebuild fails, and the route still succeeds ### Steps to Reproduce The current `main` code in `backend/open_webui/routers/files.py` does this during content updates: 1. update the file content via `process_file(..., content=...)` 2. for each knowledge collection referencing the file: - `delete(... filter={'file_id': id})` - `process_file(..., collection_name=knowledge.id)` 3. if step 2b fails, only log a warning and continue A deterministic local reproduction is: ```python from dataclasses import dataclass kb_vectors = {'kb-1': [{'file_id': 'file-1', 'chunk': 'OLD EMBEDDING'}]} @dataclass class Knowledge: id: str class VECTOR_DB_CLIENT: @staticmethod def delete(collection_name, filter): kb_vectors[collection_name] = [ v for v in kb_vectors.get(collection_name, []) if v.get('file_id') != filter['file_id'] ] class ProcessFileForm: def __init__(self, file_id, collection_name=None): self.file_id = file_id self.collection_name = collection_name def process_file(request, form, user, db=None): if form.collection_name: raise RuntimeError('reindex failed') for knowledge in [Knowledge(id='kb-1')]: try: VECTOR_DB_CLIENT.delete(collection_name=knowledge.id, filter={'file_id': 'file-1'}) process_file(None, ProcessFileForm(file_id='file-1', collection_name=knowledge.id), user=None) except Exception as e: print('swallowed knowledge reindex failure:', e) print('remaining_vectors =', kb_vectors['kb-1']) ``` Actual output: ```text swallowed knowledge reindex failure: reindex failed remaining_vectors = [] ``` The route-level call sequence is effectively: ```text process_file(file-1, content='NEW') delete(kb-1, {'file_id': 'file-1'}) process_file(file-1, collection_name='kb-1') # fails return {'content': 'NEW'} ``` ### Logs & Screenshots Relevant current code path (`backend/open_webui/routers/files.py`): ```python await ASYNC_VECTOR_DB_CLIENT.delete(collection_name=knowledge.id, filter={'file_id': id}) await process_file( request, ProcessFileForm(file_id=id, collection_name=knowledge.id), user=user, db=db, ) ``` and on failure: ```python log.warning(f'Failed to update knowledge {knowledge.id} after content change for file {id}: {e}') ``` ### Additional Information Related but not exact duplicates: - `#20558` fixed the stale-embedding case - `#6311` is about failed embeddings handling in general I have a narrow fix ready that rebuilds first and only deletes the stale vector ids after the new insert succeeds, plus a focused regression test.
Author
Owner

@shaun0927 commented on GitHub (Apr 16, 2026):

I opened a narrow fix PR for this report: #23789. The PR preserves the old KB vector ids until the replacement reindex succeeds and includes a focused regression test.

<!-- gh-comment-id:4260961227 --> @shaun0927 commented on GitHub (Apr 16, 2026): I opened a narrow fix PR for this report: #23789. The PR preserves the old KB vector ids until the replacement reindex succeeds and includes a focused regression test.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#107067