rohitg00/agentmemory · critical · Error
Embedding dimension mismatch in ${provider.name}.${where}: e
Error message
Embedding dimension mismatch in ${provider.name}.${where}: expected ${expected}, got ${v.length} What it means
agentmemory wraps every embedding provider with withDimensionGuard, which compares each returned vector's length against the provider's declared `dimensions`. If a provider returns a vector of a different length (model change, wrong dimensions param, silent upstream downgrade), the bad vector would be stored and never match anything, leaving memories silently invisible. The guard throws at the boundary instead.
Source
Thrown at src/providers/embedding/index.ts:60
return withDimensionGuard(new CohereEmbeddingProvider(getEnvVar("COHERE_API_KEY")!));
case "openrouter":
return withDimensionGuard(new OpenRouterEmbeddingProvider(getEnvVar("OPENROUTER_API_KEY")!));
case "local":
return withDimensionGuard(new LocalEmbeddingProvider());
default:
return null;
}
}
// Wrong-dimension vectors corrupt the index silently: vector-index.ts
// returns 0 from cosineSimilarity on length mismatch instead of throwing,
// so a bad vector is stored, never matches anything, and the memory
// becomes invisible without an error. Catch it at the boundary.
export function withDimensionGuard(provider: EmbeddingProvider): EmbeddingProvider {
const expected = provider.dimensions;
const check = (v: Float32Array, where: string): Float32Array => {
if (v.length !== expected) {
throw new Error(
`Embedding dimension mismatch in ${provider.name}.${where}: expected ${expected}, got ${v.length}`,
);
}
return v;
};
// Preserve the provider's prototype chain so `instanceof` checks
// against concrete classes (e.g. GeminiEmbeddingProvider) keep working.
const wrapped = Object.create(provider) as EmbeddingProvider;
wrapped.embed = async (t) => check(await provider.embed(t), "embed");
wrapped.embedBatch = async (ts) => {
const out = await provider.embedBatch(ts);
out.forEach((v, i) => check(v, `embedBatch[${i}]`));
return out;
};
if (provider.embedImage) {
wrapped.embedImage = async (s: string) =>
check(await provider.embedImage!(s), "embedImage");
}View on GitHub (pinned to e04ba88819)
Solutions
- Make provider.dimensions match the actual model output: set the *_EMBEDDING_DIMENSIONS env var or pass explicit dimensions to the model call
- Re-embed your stored memories after any model change — old vectors of a different size are incompatible
- Verify which model the endpoint actually serves (curl the API and inspect the returned vector length)
- If intentionally truncating (e.g. Matryoshka dims), ensure the provider requests the reduced dimensions from the API, not just declares them
Example fix
// before: model changed but dimensions not updated const provider = new OpenAIEmbeddingProvider(); // text-embedding-3-large returns 3072, declared 1536 // after: request and declare matching dimensions const provider = new OpenAIEmbeddingProvider(undefined, undefined, 'text-embedding-3-large'); // ensure class sets readonly dimensions = 3072 for that model
Defensive patterns
Strategy: validation
Validate before calling
function assertDimensions(provider: { name: string; dimensions: number }, vectors: Float32Array[]) {
for (const v of vectors) {
if (v.length !== provider.dimensions) {
throw new Error(`${provider.name}: expected ${provider.dimensions} dims, got ${v.length} — re-embed stored memories after model changes`);
}
}
}
// call before persisting: assertDimensions(provider, await provider.embedBatch(texts)); Type guard
const hasExpectedDimensions = (v: Float32Array, expected: number): v is Float32Array & { length: number } => v.length === expected; Try / catch
try {
const v = await provider.embed(text);
await store(id, v);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Embedding dimension mismatch')) {
logger.error({ provider: provider.name }, 'embedding model changed — schedule re-embedding');
await reEmbedAll();
} else throw err;
} Prevention
- Treat embedding model + dimensions as one atomic config unit; change them together
- Store the model name/dims alongside each stored vector and detect drift at startup
- Add a smoke test that embeds 'hello' and asserts vector length on boot
- Never hand-edit *_EMBEDDING_DIMENSIONS without verifying actual model output
When it happens
Trigger: Calling provider.embed()/embedBatch() (via embedWithProvider or memory store/recall paths wrapped by withDimensionGuard) when the returned Float32Array length differs from provider.dimensions — e.g. the configured embedding model changed its output size, OPENROUTER_EMBEDDING_DIMENSIONS was resolved incorrectly, or a proxy/base-URL override routes to a different model than declared.
Common situations: Switching OPENAI_EMBEDDING_MODEL from text-embedding-3-small (1536) to text-embedding-3-large (3072) after vectors were stored; setting OPENROUTER_EMBEDDING_DIMENSIONS to a value the model ignores; a self-hosted/proxy endpoint silently serving a different model; upgrading a local transformers model to one with a different hidden size.
Related errors
- mem::smart-search: AGENTMEMORY_AGENT_SCOPE=isolated is set b
- Unknown tool: ${toolName} (local fallback supports only ${[.
- ${envName} must be a positive integer, got: ${override}
- COHERE_API_KEY is required
- GEMINI_API_KEY is required
AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30).
Data as JSON: /api/errors/b44ec486591625de.
Report an issue: GitHub.