{"record":{"id":"44f112d66ff16e38","repo":"abhigyanpatwari/GitNexus","slug":"embedding-dimension-mismatch-endpoint-returned","errorCode":null,"errorMessage":"Embedding dimension mismatch: endpoint returned ${vec.length}d vector, but expected ${expected}d. ${hint}","messagePattern":"Embedding dimension mismatch: endpoint returned (.+?)d vector, but expected (.+?)d\\. (.+?)","errorType":"exception","errorClass":"HttpEmbeddingError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/embeddings/http-client.ts","lineNumber":674,"sourceCode":"      // into the FLOAT[N] column which would cause a cryptic Kuzu error.\n      //\n      // Unlike the cardinality check this one stays *outside* the retry loop,\n      // deliberately. The expected width is `config.dimensions ?? DEFAULT_DIMS`\n      // (GITNEXUS_EMBEDDING_DIMS), which is NOT the `dimensions` value\n      // `httpEmbedBatch` receives — that is `config.requestDimensions`, which\n      // `GITNEXUS_EMBEDDING_REQUEST_DIMS` can set to a different number or to\n      // `undefined` (`omit`). More importantly a width mismatch is an operator\n      // *configuration* error, not an endpoint fault: retrying it three times\n      // can never change the answer, and routing it through the retry loop\n      // would count a healthy endpoint's responses toward the shared circuit\n      // breaker. The message is an actionable config hint, so it is terminal\n      // on the first attempt by design (#2790).\n      const expected = config.dimensions ?? DEFAULT_DIMS;\n      if (vec.length !== expected) {\n        const hint = config.dimensions\n          ? 'Update GITNEXUS_EMBEDDING_DIMS to match your model output.'\n          : `Set GITNEXUS_EMBEDDING_DIMS=${vec.length} to match your model output.`;\n        throw new HttpEmbeddingError(\n          `Embedding dimension mismatch: endpoint returned ${vec.length}d vector, ` +\n            `but expected ${expected}d. ${hint}`,\n        );\n      }\n\n      allVectors.push(vec);\n    }\n  }\n\n  return allVectors;\n};\n\n/**\n * Embed a single query text via the HTTP backend.\n * Convenience for MCP search where only one vector is needed.\n *\n * @param text - Query text to embed\n * @returns Embedding vector as number array","sourceCodeStart":656,"sourceCodeEnd":692,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/core/embeddings/http-client.ts#L656-L692","documentation":"HttpEmbeddingError thrown during httpEmbed() when the endpoint returns a vector whose width differs from the expected width (config.dimensions from GITNEXUS_EMBEDDING_DIMS, defaulting to 384). This is deliberately terminal on first occurrence, not retried (#2790): a width mismatch is an operator configuration error, not a transient endpoint fault, and retrying cannot change the vector width. The message embeds the actual width and an actionable hint.","triggerScenarios":"Calling httpEmbed() after switching the embedding model (e.g. from a 384-dim MiniLM model to a 768-dim or 1536-dim model) without updating GITNEXUS_EMBEDDING_DIMS; or explicitly setting GITNEXUS_EMBEDDING_DIMS=384 while pointing at a model that outputs another width; or a server that ignores the requested dimensions parameter.","commonSituations":"Swapping a local Ollama model for an OpenAI text-embedding-3-* model (1536/3072 dims) while the index and env still assume 384; a gateway silently substituting a different model version; re-using an old .env against a new endpoint. Note the vector width is baked into the existing index, so changing dims requires re-embedding.","solutions":["Read the number in the message and set it explicitly: export GITNEXUS_EMBEDDING_DIMS=<returned-width> (the hint line already contains the exact assignment).","If GITNEXUS_EMBEDDING_DIMS was explicitly set to the wrong value, update it to match the model's actual output width.","After changing dims, re-run the full analyze/embedding pass so stored vectors match the new width; mixing widths corrupts similarity search.","If the server should honor a dimensions request but does not, verify with curl that the dimensions field is accepted, and use GITNEXUS_EMBEDDING_REQUEST_DIMS only for what is sent on the wire."],"exampleFix":"# before\nexport GITNEXUS_EMBEDDING_MODEL=text-embedding-3-small   # returns 1536d\n# GITNEXUS_EMBEDDING_DIMS unset -> expects default 384\ngitnexus analyze  # -> Embedding dimension mismatch: endpoint returned 1536d vector, but expected 384d.\n\n# after\nexport GITNEXUS_EMBEDDING_DIMS=1536\ngitnexus analyze  # fresh index with 1536d vectors","handlingStrategy":"validation","validationCode":"// Pre-flight: embed one probe string and pin GITNEXUS_EMBEDDING_DIMS to the answer.\nimport { isHttpMode } from './core/embeddings/http-client.js';\n\nexport async function resolveEmbeddingDims(): Promise<number> {\n  if (!isHttpMode()) return 0; // local embedder path\n  const base = process.env.GITNEXUS_EMBEDDING_URL!.replace(/\\/+$/, '');\n  const res = await fetch(`${base}/embeddings`, {\n    method: 'POST',\n    headers: {\n      'content-type': 'application/json',\n      authorization: `Bearer ${process.env.GITNEXUS_EMBEDDING_API_KEY ?? 'unused'}`,\n    },\n    body: JSON.stringify({ model: process.env.GITNEXUS_EMBEDDING_MODEL, input: ['ping'] }),\n  });\n  if (!res.ok) throw new Error(`dims probe failed: ${res.status}`);\n  const { data } = (await res.json()) as { data: { embedding: number[] }[] };\n  return data[0].embedding.length;\n}","typeGuard":"import { HttpEmbeddingError } from './core/embeddings/http-client.js';\n\nexport const isDimensionMismatchError = (e: unknown): e is HttpEmbeddingError =>\n  e instanceof HttpEmbeddingError && e.message.startsWith('Embedding dimension mismatch');","tryCatchPattern":"try {\n  vectors = await httpEmbed(texts);\n} catch (err) {\n  if (isDimensionMismatchError(err)) {\n    // Terminal config error — do NOT retry; dims cannot change between calls.\n    const actual = /returned (\\d+)d/.exec(err.message)?.[1];\n    throw new Error(\n      `Model width changed. Re-run with GITNEXUS_EMBEDDING_DIMS=${actual} and rebuild the index`,\n      { cause: err },\n    );\n  }\n  throw err;\n}","preventionTips":["Always set GITNEXUS_EMBEDDING_DIMS explicitly — never rely on the 384 default with hosted models.","Run the one-string dims probe at pipeline start and fail if it disagrees with GITNEXUS_EMBEDDING_DIMS.","Treat model changes as index-breaking: change model + dims together, then re-analyze from scratch.","Keep a single source of truth (one .env) shared by indexing and search processes so both see identical dims."],"tags":["embeddings","configuration","dimension-mismatch"],"backgroundTag":"embedding-dimension-mismatch","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}