abhigyanpatwari/GitNexus · error

${resume.error}

Error message

${resume.error}

What it means

During an embedding pass, `decideEmbeddingResume` inspects the prior run's embedding checkpoint. When its verdict is `action: 'abort'` — the checkpoint cannot be safely resumed (e.g. embedding identity/provider mismatch where resuming would mix vector spaces, or a fail-closed condition) — the API throws with the decision's pre-computed, user-facing `resume.error` message rather than silently discarding or mixing embeddings.

Solutions

  1. Read `resume.error` — it names the identity mismatch; align the current embedding provider/model/dimensions with the checkpoint.
  2. Re-run embedding with the force/discard path (`--embeddings --force` or the API equivalent) to discard the checkpoint and rebuild from scratch.
  3. Run `gitnexus analyze --drop-embeddings` to clear the stale checkpoint, then re-embed with the desired identity.
  4. Verify the checkpoint's stored identity against your current config before re-triggering the embed endpoint.

Example fix

// before: provider changed but old checkpoint forces abort
POST /api/embed  ->  Error: checkpoint was written with provider 'openai/text-embedding-3-small'; current run uses 'ollama/...'
// after: discard the incompatible checkpoint first
$ gitnexus analyze --embeddings --force
Defensive patterns

Strategy: try-catch

Validate before calling

// compare the checkpoint's stored identity with the current run identity before embedding
import { decideEmbeddingResume } from './core/embedding-checkpoint';
const decision = priorCheckpoint ? decideEmbeddingResume(priorCheckpoint, currentIdentity) : undefined;
if (decision?.action === 'abort') {
  // handle up front: force/discard or realign provider config
}

Type guard

const isAbort = (d) => d?.action === 'abort' && typeof d.error === 'string';

Try / catch

try {
  await embedRepo(entry);
} catch (e) {
  if (isEmbeddingIdentityAbort(e)) {
    logger.warn('Embedding identity mismatch — discarding checkpoint and re-embedding from scratch');
    await embedRepo(entry, { force: true });
  } else throw e;
}

Prevention

When it happens

Trigger: `POST /api/embed` (or the embeddings sync) finds a `priorCheckpoint` whose kind requires the current run's embedding identity and the identity does not match (different model/provider/dimensions), so `decideEmbeddingResume` returns `{action:'abort', error}` and `throw new Error(resume.error)` fires at gitnexus/src/server/api.ts:2033.

Common situations: Switching embedding providers or models between runs and re-embedding the same repo; changing embedding dimensions in config; a checkpoint written by a different GitNexus version with an incompatible identity; re-pointing the repo at a different embedding endpoint.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/f023c4673cc7dcf4. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/server/api.ts:2033

                const embeddingIdentity = resolveEmbeddingIdentity();
                if (!embeddingMeta) {
                  throw new Error('Repository metadata is missing; run gitnexus analyze first');
                }
                const priorCheckpoint = embeddingMeta.embeddingCheckpoint;
                // The SAME decision the CLI's resume gate makes
                // (core/embedding-checkpoint.ts). This route used to hard-throw on
                // any identity mismatch and ignore `attempts` entirely, so a
                // `'partial'` marker written by `gitnexus analyze` and resumed
                // here hit exactly the permanent wedge `kind` exists to remove:
                // two readers of one record disagreeing about the rule it encodes.
                // No `force`/`--drop-embeddings` equivalent exists on this route,
                // so the flag options go unset and `'discard'` is unreachable —
                // it is folded into the abandon arm rather than given an invented
                // flag. `maxAttempts` is left to the shared default.
                const resume = priorCheckpoint
                  ? decideEmbeddingResume(priorCheckpoint, embeddingIdentity)
                  : undefined;
                if (resume?.action === 'abort') throw new Error(resume.error);
                if (resume?.action === 'abandon' || resume?.action === 'discard') {
                  logger.warn({ repo: entry.name }, resume.log);
                }
                const forceReembedNodeIds: ReadonlySet<string> =
                  resume?.action === 'resume' ? resume.pendingNodeIds : new Set<string>();
                const saveEmbeddingCheckpoint = async (
                  checkpoint: {
                    nodesProcessed: number;
                    totalNodes: number;
                    chunksProcessed: number;
                  },
                  pendingNodeIds: string[],
                  embeddings?: PersistedEmbeddingCount,
                ): Promise<void> => {
                  // tri-review NEW-2: re-read immediately before writing (mirrors
                  // the pattern in run-analyze.ts's --repair-fts stamp) instead of
                  // spreading the stale `embeddingMeta` snapshot captured once at
                  // job start. This job can run up to EMBED_TIMEOUT_MS (30 min);

View on GitHub (pinned to ac9a4e9abd)