Cached document embeddings shift to the wrong inputs after an invalid vector is skipped
- Dominant language
- TypeScript
- Stars
- 156k
- Forks
- 24.6k
- Avg merge
- 22h 9m
- Merged PRs (30d)
- 610
Description
### Self Checks
- [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general).
- [x] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones.
- [x] I confirm that I am using English to submit this report, otherwise it will be closed.
- [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
- [x] Please do not modify this template :) and fill in all the required fields.
### Dify version
Current `main` at `09054169c42100b8ff5ebc1163636220fa7162bc`.
### Cloud or Self Hosted
Self Hosted (Source)
### Steps to reproduce
1. Call `CacheEmbedding.embed_documents()` with two or more uncached inputs.
2. Have the embedding provider return an invalid vector that normalizes to NaN for an earlier input and a valid vector for a later input.
3. Inspect the returned list and the persisted `Embedding` rows.
The same behavior is reproducible through `embed_multimodal_documents()`.
This trigger is production-reachable: #11827 reported an Ollama provider occasionally returning an all-zero embedding, which normalizes to NaN. The existing skip guard added by #11840 prevents that invalid vector from being cached, but exposes the alignment bug described here.
### ✔️ Expected Behavior
The invalid vector should remain skipped in its own input slot, while every later valid vector should stay paired with and cached under its original input.
### ❌ Actual Behavior
The invalid vector is removed from `embedding_queue_embeddings`, after which the shortened list is zipped with the original `embedding_queue_indices`. The next valid vector is therefore returned in the invalid input's slot and persisted under the wrong text hash or file ID. Its own input slot remains `None`.
This silently corrupts document/vector alignment and can degrade retrieval after a provider returns one zero or NaN vector during batched indexing.
I reproduced this with focused SQLite and mocked-provider regression tests for both text and multimodal document embeddings. The tests fail on current `main` with the later valid vector in the wrong slot and pass when each successfully normalized vector retains its original batch index. The fix preserves the existing behavior of logging the invalid vector and leaving its slot as `None`.
If this direction is acceptable, please assign the issue to me so I can submit the tested patch.
AI disclosure: I used Codex to help analyze the code and prepare the reproduction. I reviewed the findings, patch, and tests and am responsible for this report.
Contributor guide
Research direction
Start at CacheEmbedding.embed_documents() and embed_multimodal_documents(), then run the focused SQLite and mocked-provider regression tests described in the report. Check how invalid vectors are skipped before results are paired with input indices and persisted Embedding rows. Done means invalid inputs remain None while later valid vectors retain their original slots and hashes or file IDs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sqlite
- Domain
- backend, machine-learning
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100