abhigyanpatwari/GitNexus · error · Error

Cannot resume embedding checkpoint: the embedding provider c

Error message

Cannot resume embedding checkpoint: the embedding provider configuration differs. Restore the matching endpoint configuration or pass --drop-embeddings to rebuild without it.

What it means

Thrown by `decideEmbeddingResume` when the checkpoint's `provider` field differs from the current run's resolved provider AND the checkpoint kind is `'interrupted'`. An 'interrupted' marker means its pending nodes may hold a subset of their chunks, so resuming under a different provider would mix two embedding vector spaces — a silent semantic-search corruption. The function fails closed rather than risk the mix.

Source

Thrown at gitnexus/src/core/run-analyze.ts:1315

  let resumedEmbeddingCheckpoint: EmbeddingCheckpoint | undefined;
  if (existingMeta?.embeddingCheckpoint) {
    const checkpoint = existingMeta.embeddingCheckpoint;
    // The verdict itself lives in embedding-checkpoint.ts, shared with
    // `POST /api/embed` — two readers of one marker must not be able to
    // disagree about what it means.
    //
    // The identity stays LAZY, as it has to: the flag and retry-budget verdicts
    // short-circuit before one is needed, and resolving it means importing an
    // embeddings module (#2370 — none loads unless a run actually needs one).
    // `decideEmbeddingResume` asks for it by aborting on `undefined`, which is
    // the only abort it can reach without one.
    let decision = decideEmbeddingResume(checkpoint, undefined, options);
    if (decision.action === 'abort') {
      const { resolveEmbeddingIdentity } = await import('./embeddings/embedding-identity.js');
      embeddingIdentityForRun = resolveEmbeddingIdentity();
      decision = decideEmbeddingResume(checkpoint, embeddingIdentityForRun, options);
    }
    if (decision.action === 'abort') throw new Error(decision.error);
    log(decision.log);
    if (options.dropEmbeddings) {
      // --drop-embeddings has always implied a rebuild here; the decision only
      // covers the marker.
      options = { ...options, force: true };
    }
    if (decision.action === 'resume') {
      resumeEmbeddingCheckpoint = true;
      pendingEmbeddingNodeIds = new Set(decision.pendingNodeIds);
      resumedEmbeddingCheckpoint = decision.resumedFrom;
    }
  }

  // ── Crash recovery: dirty flag forces full rebuild ────────────────
  // If the previous incremental run set incrementalInProgress and didn't
  // clear it, the on-disk index may be in a half-state. Cheapest path
  // back to a known-good index is to wipe + rebuild from scratch.
  if (existingMeta?.incrementalInProgress) {

View on GitHub (pinned to d540b00184)

Solutions

  1. Restore the original embedding provider configuration (`GITNEXUS_EMBEDDING_URL` and related provider env vars) to match the checkpoint, then re-run `gitnexus analyze`.
  2. If the provider change is intentional, run `gitnexus analyze --drop-embeddings` to discard the old checkpoint and rebuild under the new provider.
  3. Run `gitnexus analyze --force` for a full rebuild (this also discards the checkpoint).

Example fix

// before
// checkpoint provider=openai, current run provider=ollama
gitnexus analyze
// error: ...embedding provider configuration differs.
// after (intentional switch)
gitnexus analyze --drop-embeddings
// or (restore original)
export GITNEXUS_EMBEDDING_URL=https://api.openai.com/v1
gitnexus analyze
Defensive patterns

Strategy: validation

Validate before calling

// Before analyze, compare checkpoint provider with current env:
import { loadMeta } from './storage/repo-manager.js';
import { checkpointKind } from './embedding-checkpoint.js';
const meta = await loadMeta(metaDir);
const cp = meta?.embeddingCheckpoint;
if (cp && checkpointKind(cp) === 'interrupted') {
  const currentProvider = process.env.GITNEXUS_EMBEDDING_PROVIDER ?? deriveProviderFromUrl(process.env.GITNEXUS_EMBEDDING_URL);
  if (currentProvider && cp.provider !== currentProvider) {
    console.error(`Provider mismatch: checkpoint=${cp.provider}, current=${currentProvider}.`);
    console.error('Restore matching config or pass --drop-embeddings.');
    process.exit(1);
  }
}

Type guard

import { checkpointKind } from './embedding-checkpoint.js';
const isProviderMismatchFatal = (
  checkpoint: EmbeddingCheckpoint,
  currentProvider: string,
): boolean =>
  checkpointKind(checkpoint) === 'interrupted' &&
  checkpoint.provider !== currentProvider;

Try / catch

try {
  await runAnalyze(options);
} catch (err) {
  if (err instanceof Error && err.message.includes('embedding provider configuration differs')) {
    console.error('Restore the original provider config or run `gitnexus analyze --drop-embeddings`.');
  }
  throw err;
}

Prevention

When it happens

Trigger: `decideEmbeddingResume` is called with a resolved `identity` where `checkpoint.provider !== identity.provider`, the checkpoint kind is `'interrupted'`, and neither `--force` nor `--drop-embeddings` was passed (both short-circuit earlier).

Common situations: A team member indexed with an OpenAI embedding endpoint, left an interrupted checkpoint, then a colleague's environment points to a local Ollama provider; or the endpoint URL env var was changed between runs without `--drop-embeddings`.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/021c57ece9f752bb. Report an issue: GitHub.