FAISS RAG pipeline chunk overlap configuration issues

Problem – Retrieval Degradation from Improper Chunk Overlap

In a local development sandbox a Retrieval‑Augmented Generation (RAG) pipeline builds a FAISS index from a corpus of text documents. The chunk_size and overlap parameters used during document splitting are mis‑configured, causing two distinct symptoms:

  • Excessive overlap (e.g., overlap = 0.9 * chunk_size) produces many near‑duplicate vectors. Queries return 4‑15 redundant passages, precision drops, and latency rises because the index must compare many almost identical vectors.
  • Insufficient overlap (e.g., overlap = 0) breaks sentence boundaries. Queries retrieve only the first segment of multi‑paragraph sections, leading to incomplete answers.

Typical log excerpts:


[2026-07-14 10:12:03] WARN faiss.index: Duplicate vectors detected (overlap too high)
[2026-07-14 10:15:47] ERROR faiss.index: No results found for query (overlap = 0)
[2026-07-14 10:20:11] ERROR faiss.index: Invalid argument: dimension mismatch

Root Cause – How Chunk Overlap Interacts with FAISS Indexing

The FAISS index stores a vector per text chunk. Overlap determines how much content is shared between consecutive chunks. When overlap is too high, the embedding model generates almost identical vectors for the overlapping region, and FAISS treats them as separate entries. According to the FAISS Index construction guide, duplicate vectors inflate recall but hurt precision because the nearest‑neighbor search returns many redundant results.

Conversely, zero overlap can split sentences or paragraphs, producing embeddings that fall outside the distribution learned during training. The FAISS training documentation notes that the coarse quantizer of IVF indexes expects vectors that respect the original data granularity; breaking that granularity leads to the “No results found” error.

Additionally, changing the chunk size or overlap without rebuilding or retraining the index violates the assumptions documented for IndexIVFFlat and IndexFlatL2 (see the Python API reference). The index dimension must match the embedding dimension, and the index must be retrained after any change that alters the number of vectors per document.

Debug – Systematic Investigation Steps

1. Verify Chunking Parameters

python -c "
from my_rag.pipeline import Chunker
c = Chunker(chunk_size=1000, overlap=0)   # current config
print(f'Chunk size: {c.chunk_size}, Overlap: {c.overlap}')
"

Check that overlap is expressed as an absolute number of tokens, not a fraction.

2. Inspect Index Statistics

python - <<'PY'
import faiss, numpy as np
index = faiss.read_index('faiss.index')
print('ntotal:', index.ntotal)
print('is_trained:', index.is_trained)
# Approximate duplicate count (vectors with distance < 1e-6)
D, I = index.search(np.zeros((1, index.d)), 10)
print('first 10 distances:', D[0])
PY

If many distances are near zero, duplicate vectors are present.

3. Sample Retrieval Output

curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{"query":"Explain quantum entanglement"}' | jq .

Observe whether the returned passages contain repeated sentences.

4. Check Index Build Logs


2026-07-14 09:58:12 INFO faiss.index: Adding 12457 vectors (dim=768)
2026-07-14 09:58:13 WARN faiss.index: Duplicate vectors detected (count=3421)
2026-07-14 09:58:14 INFO faiss.index: Index training completed

5. Validate Embedding Dimension Consistency

python - <<'PY'
from my_rag.embeddings import embed_text
vec = embed_text("test")
print('Embedding dim:', len(vec))
PY

If the dimension differs from index.d, the “Invalid argument: dimension mismatch” error will appear.

Solution – Correcting Chunk Overlap and Index Lifecycle

1. Choose a Balanced Overlap

Empirical results from the community (GitHub issue #3221) suggest an overlap of 10‑20% of chunk_size provides enough context continuity without creating duplicates.

# Before (excessive overlap)
chunker = Chunker(chunk_size=1000, overlap=int(0.9 * 1000))

# After (balanced overlap)
chunker = Chunker(chunk_size=1000, overlap=200)  # 20% overlap

2. Re‑train and Re‑build the Index After Changing Parameters

# Re‑train IVF coarse quantizer
index = faiss.IndexIVFFlat(faiss.IndexFlatL2(d), nlist=100, metric=faiss.METRIC_L2)
index.train(embedding_matrix)   # embedding_matrix shape: (num_chunks, d)

# Add vectors
index.add(embedding_matrix)
faiss.write_index(index, 'faiss.index')

Skipping the train() step triggers the “Index not trained” error (see common errors list).

3. Clear Stale Vectors Before Re‑indexing

import os
if os.path.exists('faiss.index'):
    os.remove('faiss.index')
# Proceed with fresh index creation

4. Align Embedding Dimension After Tokenizer Changes

If the tokenizer or embedding model changes (e.g., from 768‑dim to 1024‑dim), rebuild the index with the new dimension:

# Example with new model
new_dim = 1024
index = faiss.IndexFlatL2(new_dim)
# Add new vectors
index.add(new_embeddings)

Verify – Confirming the Fix

Functional Validation

curl -X POST http://localhost:8000/query \
  -H "Content-Type: application/json" \
  -d '{"query":"Explain quantum entanglement"}' | jq .

Expected outcome: returned passages are distinct, cover the full multi‑paragraph answer, and no sentence repeats.

Metric Checks

  • Duplicate ratio: run a script to compute the proportion of vectors with distance < 1e-5. Target < 1%.
  • Recall vs. Precision: use a held‑out test set and compute R@5 and P@5. Precision should improve noticeably after reducing overlap.
  • Latency: measure average query time before and after. Expect a reduction of ~10‑20% when duplicates are removed.

Prevent – Guardrails for Future Development

  • Encapsulate chunking logic in a reusable class with explicit validation:
    if not 0 < overlap < chunk_size:
        raise ValueError('Overlap must be between 0 and chunk_size') 
    
  • Automate index rebuilding in CI/CD when chunk_size, overlap, or embedding model version changes.
  • Add a health‑check endpoint that verifies:
    • index.is_trained == True
    • index.d == current_embedding_dim
    • duplicate_ratio < 0.01
  • Instrument logging for duplicate detection:
    if duplicate_count > 0:
        logger.warning('Duplicate vectors detected (%d)', duplicate_count)
    
  • Set up alerts on the “Index not trained” and “Invalid argument: dimension mismatch” log patterns.

FAQ – Common Follow‑up Questions

  1. Why does setting overlap = 0 sometimes return no results?
    Zero overlap can split sentences, producing embeddings that fall outside the distribution learned by the quantizer. FAISS then fails to find close neighbors, leading to “No results found for query”. Use a small non‑zero overlap (e.g., 10% of chunk_size) to preserve sentence continuity.
  2. How can I detect duplicate vectors in an existing index?
    Run a nearest‑neighbor search on a random subset of vectors and count distances below a tiny threshold (e.g., 1e-6). A high duplicate ratio indicates excessive overlap.
  3. Do I need to retrain the IVF index when I only change overlap?
    Yes. Overlap changes the number of vectors and their distribution. The coarse quantizer must be retrained to reflect the new vector set; otherwise you’ll see “Index not trained”.
  4. What is the recommended chunk size for long documents?
    Choose a size that fits the embedding model’s token limit (e.g., 512‑1024 tokens) and apply 10‑20% overlap. Adjust based on empirical recall/precision trade‑offs for your corpus.
  5. My pipeline switched to a new embedding model but the index still uses the old dimension. How do I fix the “Invalid argument: dimension mismatch” error?
    Delete the existing index file, instantiate a new FAISS index with the new dimension (e.g., IndexFlatL2(new_dim)), and rebuild it using embeddings generated by the new model.

Related Topic Hub: Vector Databases Troubleshooting Hub