langgenius / langgenius/dify

Cached document embeddings shift to the wrong inputs after an invalid vector is skipped

Open
#41,476 1 comment 1 reaction 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.