Egonex-AI/Understand-Anything · error · Error

Current edge candidates do not match the incremental plan; r

Error message

Current edge candidates do not match the incremental plan; rerun merge

What it means

Before computing removed/retained nodes for the retry, the script checks that the persisted incremental-edge-candidates.json matches the current plan's baseCommit and headCommit and contains an edges array. A mismatch means the candidates file is from a different (older) incremental run, so it throws and instructs re-running the merge to regenerate candidates in sync with the plan.

Source

Thrown at understand-anything-plugin/skills/understand/prepare-symbol-retry.mjs:74

    batch.previousSymbols = baseline.files.filter(file => files.has(file.filePath)).flatMap(file =>
      file.nodes.filter(symbolKind).map(node => {
        const parents = new Set(file.edges.filter(edge => edge.type === 'contains' && edge.target === node.id)
          .map(edge => edge.source));
        const owners = file.nodes.filter(parent => parent.type === 'class' && parents.has(parent.id))
          .map(parent => parent.name);
        return {
          id: node.id, name: node.name, type: node.type, filePath: node.filePath,
          ...(node.lineRange ? { lineRange: node.lineRange } : {}),
          ...(owners.length ? { owners } : {}),
        };
      }),
    );
    batch.missingSymbols = report.files.filter(file => files.has(file.filePath));
  }
  const assembled = readJson(join(intermediateDir, 'assembled-graph.json'));
  const candidates = readJson(join(intermediateDir, 'incremental-edge-candidates.json'));
  if (candidates.baseCommit !== plan.baseCommit || candidates.headCommit !== plan.headCommit
    || !Array.isArray(candidates.edges)) throw new Error('Current edge candidates do not match the incremental plan; rerun merge');
  const removedIds = new Set(assembled.nodes.filter(node => paths.has(normalizePath(node.filePath)))
    .map(node => node.id));
  const retainedIds = new Set(assembled.nodes.filter(node => !removedIds.has(node.id)).map(node => node.id));
  const currentNodes = new Map(assembled.nodes.map(node => [node.id, node]));
  const previousIds = new Set(baseline.files.filter(file => paths.has(file.filePath))
    .flatMap(file => file.nodes.map(node => node.id)));
  const targetsAffectedFile = id => removedIds.has(id) || previousIds.has(id)
    || (typeof id === 'string' && [...paths].some(path => id.includes(`:${path}:`)));
  const sourceRemap = new Map(report.files.flatMap(file => file.replacements)
    .map(({ oldId, newId }) => [oldId, newId]));
  const inboundEdgeCandidates = [...assembled.edges, ...candidates.edges]
    .map(edge => ({ ...edge, source: currentNodes.has(edge.source) ? edge.source : sourceRemap.get(edge.source) ?? edge.source })).filter(edge =>
    retainedIds.has(edge.source) && targetsAffectedFile(edge.target));
  const contextPaths = new Set(paths);
  for (const edge of inboundEdgeCandidates) {
    for (const id of [edge.source, edge.target]) {
      if (currentNodes.has(id)) contextPaths.add(normalizePath(currentNodes.get(id).filePath));
    }

View on GitHub (pinned to 07edf82a04)

Solutions

  1. Re-run the incremental merge step so incremental-edge-candidates.json is regenerated for the current base/head commits.
  2. Clear stale intermediate files (incremental-edge-candidates.json) and re-run prepare-incremental.mjs followed by prepare-symbol-retry.mjs.
  3. Verify you are operating on the same commits as the plan — if HEAD moved, re-prepare the incremental plan first.
  4. Check plugin version consistency: old candidates schema (missing edges array) requires regenerating with the current version.

Example fix

// before: candidates from older run
// candidates: {baseCommit:'aaa', headCommit:'bbb'}, plan: {baseCommit:'aaa', headCommit:'ccc'} → throw
// after: regenerate candidates via merge
node prepare-incremental.mjs .   # rewrites incremental-edge-candidates.json for current plan
node prepare-symbol-retry.mjs .
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
const candidatesPath = join(intermediateDir, 'incremental-edge-candidates.json');
if (!existsSync(candidatesPath)) throw new Error('Edge candidates missing; rerun merge');
const c = JSON.parse(readFileSync(candidatesPath, 'utf-8'));
if (c.baseCommit !== plan.baseCommit || c.headCommit !== plan.headCommit || !Array.isArray(c.edges)) {
  throw new Error('Stale edge candidates; rerun incremental merge first');
}

Type guard

const candidatesMatchPlan = (candidates, plan) =>
  candidates && candidates.baseCommit === plan.baseCommit
  && candidates.headCommit === plan.headCommit
  && Array.isArray(candidates.edges);

Try / catch

try {
  await runRetry(projectRoot);
} catch (e) {
  if (e.message.includes('Current edge candidates do not match')) {
    // rerun prepare-incremental.mjs to regenerate candidates for the current plan
  } else throw e;
}

Prevention

When it happens

Trigger: Running prepare-symbol-retry.mjs when incremental-edge-candidates.json was written for a different base/head commit pair than the loaded plan, was not produced by the merge step, or is missing the edges array (older schema or partial write).

Common situations: Leftover candidates file from a previous incremental run after commits advanced; interrupted merge step that never wrote candidates for the current plan; hand-copied intermediate state between machines/branches; older plugin version writing a different candidates schema.

Related errors


AI-assisted analysis of Egonex-AI/Understand-Anything@07edf82a04 (2026-09-07). Data as JSON: /api/errors/52906b3a27740436. Report an issue: GitHub.