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
- Read `resume.error` — it names the identity mismatch; align the current embedding provider/model/dimensions with the checkpoint.
- Re-run embedding with the force/discard path (`--embeddings --force` or the API equivalent) to discard the checkpoint and rebuild from scratch.
- Run `gitnexus analyze --drop-embeddings` to clear the stale checkpoint, then re-embed with the desired identity.
- 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
- Keep embedding provider/model/dimension config stable for a repo between runs.
- When intentionally switching providers, always pass the force/discard option to clear the old checkpoint.
- Inspect `gitnexus status` checkpoint metadata before re-embedding after a config change.
- Store embedding identity in infra-as-code so drift between environments is caught early.
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
- decision.error (embedding resume decision abort)
- analyze --watch does not support
- analyze --watch does not support
- assessment.message (local embedding runtime…
- Cannot sync embeddings: the index checkpoint was written by
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)