Skip to content

Bug: embedding fails with 400 on long content (no chunking or size handling) #58

Description

@johnathanneals-dev

Summary

create_embedding() passes the full text to the embedding provider with no size handling. When the input exceeds the model's context window (e.g., 2048 tokens for nomic-embed-text, 8191 for text-embedding-3-small), the API returns a 400 error and the memory store operation fails silently or crashes.

This affects any memory that exceeds the model's token limit — long conversation transcripts, detailed technical notes, multi-section documents.

Reproduction

from src.embedder import create_embedding

long_text = "detailed technical content... " * 2000  # ~10KB
embedding = create_embedding(long_text)  # 400 error from provider

With nomic-embed-text (Ollama, 2048-token context):

requests.exceptions.HTTPError: 400 Client Error: {"error":"the input length exceeds the context length"}

With OpenRouter/OpenAI models the threshold is higher (~8K tokens) but the same crash occurs on sufficiently long content.

Root cause

src/embedder/__init__.py — the embed() method on every provider class sends the full input text without checking length or chunking:

def embed(self, text: str) -> List[float]:
    response = requests.post(
        f'{self.base_url}/embeddings',
        json={'model': self.model, 'input': text},
        ...
    )

No provider implementation has size checking, truncation, or chunking.

Suggested fix

Add automatic chunking with mean-pooling at the create_embedding() level (above the provider abstraction, so all providers benefit):

  1. Split text exceeding max_chars into overlapping chunks
  2. Embed each chunk independently
  3. Mean-pool + L2-normalize the chunk embeddings into a single vector

This preserves the single-vector-per-memory contract while handling arbitrarily long input. The full text is still stored in the database — only the embedding generation is affected.

A conservative default of ~4000 chars safely fits within nomic-embed-text's 2048-token window even for dense technical content (which tokenizes at ~2-3 chars/token). Higher-capacity models like text-embedding-3-small could use a larger threshold via configuration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions