Egonex-AI/Understand-Anything · error · Error

Retry endpoint descriptors are missing

Error message

Retry endpoint descriptors are missing

What it means

When `incremental-symbol-retry.json` matches the plan's commit range and contains an `inboundEdgeCandidates` array, it is treated as a valid retry record — which also requires a `currentFiles` array holding the endpoint descriptors for the retry. If `currentFiles` is missing or not an array while the rest looks valid, the retry record is internally incomplete and the validator throws, since inbound edge candidates cannot be resolved without endpoint file descriptors.

Source

Thrown at understand-anything-plugin/skills/understand/validate-incremental-symbols.mjs:386

        }
      }
      const result = compareFileSymbols(previous, current, baseEvidence, headEvidence);
      report.files.push(result);
      if (result.missing.some(node => node.status !== 'deleted')) report.unresolvedFiles.push(previous.filePath);
    }
    report.ok = report.errors.length === 0 && report.unresolvedFiles.length === 0;
    if (report.ok) {
      const retryPath = join(intermediateDir, 'incremental-symbol-retry.json');
      const retry = existsSync(retryPath) ? readJson(retryPath) : null;
      const candidatePath = join(intermediateDir, 'incremental-edge-candidates.json');
      const currentCandidates = existsSync(candidatePath) ? readJson(candidatePath) : null;
      if (currentCandidates && (currentCandidates.baseCommit !== plan.baseCommit
        || currentCandidates.headCommit !== plan.headCommit || !Array.isArray(currentCandidates.edges))) {
        throw new Error('Current edge candidates do not match the incremental plan');
      }
      const hasRetry = retry?.baseCommit === plan.baseCommit && retry.headCommit === plan.headCommit
        && Array.isArray(retry.inboundEdgeCandidates);
      if (hasRetry && !Array.isArray(retry.currentFiles)) throw new Error('Retry endpoint descriptors are missing');
      const candidates = [
        ...(currentCandidates?.edges ?? []).map(edge => ({ edge, saved: false })),
        ...(hasRetry ? retry.inboundEdgeCandidates : []).map(edge => ({ edge, saved: true })),
      ];
      if (candidates.length || hasRetry) {
        const ids = new Set(graph.nodes.map(node => node.id));
        const replacements = new Map(report.files.flatMap(file => file.replacements)
          .map(({ oldId, newId }) => [oldId, newId]));
        const deleted = new Set(report.files.flatMap(file => file.missing.map(node => node.id)));
        const baselineBindings = new Map(baseline.files.flatMap(file => file.nodes.filter(symbolKind))
          .map(node => [node.id, deleted.has(node.id) ? null : replacements.get(node.id) ?? node.id]));
        const currentBindings = new Map();
        // These descriptors belong to the initial CURRENT analysis, not the
        // old published graph. Both sides therefore map against HEAD source.
        for (const previous of hasRetry ? retry.currentFiles : []) {
          const current = fileGraph(previous.filePath);
          const needsEvidence = previous.nodes.some(node => symbolKind(node) && !hasPreservedIdentity(node, previous, current, true));
          const evidence = needsEvidence && previous.filePath ? await parseHead(previous.filePath) : undefined;

View on GitHub (pinned to 07edf82a04)

Solutions

  1. Delete `incremental-symbol-retry.json` and re-run the step that produces it so both `inboundEdgeCandidates` and `currentFiles` are written atomically.
  2. Inspect the retry file (`jq 'keys' incremental-symbol-retry.json`) to confirm which fields survived and fix the interrupted writer.
  3. Make the retry writer emit the file in one atomic write (write temp file + rename) to avoid partial records.
  4. If a schema rename caused it, align the producer and validator on the `currentFiles` field name.

Example fix

// before: incomplete retry record
{ "baseCommit": "a", "headCommit": "b", "inboundEdgeCandidates": [...] }

// after: regenerate with both fields written atomically
rm .ua/intermediate/incremental-symbol-retry.json
# re-run retry recording => { baseCommit, headCommit, inboundEdgeCandidates: [...], currentFiles: [...] }
Defensive patterns

Strategy: validation

Validate before calling

const rPath = join(dir,'incremental-symbol-retry.json');
if (existsSync(rPath)) {
  const r = JSON.parse(readFileSync(rPath,'utf8'));
  const hasInbound = Array.isArray(r.inboundEdgeCandidates);
  if (hasInbound && !Array.isArray(r.currentFiles)) {
    unlinkSync(rPath); // incomplete retry record; force regeneration
    await rerunRetryRecording();
  }
}

Type guard

function isCompleteRetryRecord(plan, r) {
  return r != null && r.baseCommit === plan.baseCommit
    && r.headCommit === plan.headCommit
    && Array.isArray(r.inboundEdgeCandidates)
    && Array.isArray(r.currentFiles);
}

Try / catch

try {
  await validateIncrementalSymbols({ projectRoot, intermediateDir });
} catch (err) {
  if (err.message === 'Retry endpoint descriptors are missing') {
    rmSync(join(intermediateDir, 'incremental-symbol-retry.json'));
    await rerunRetryRecording(); // rewrite with currentFiles included
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `validateIncrementalSymbols` when `incremental-symbol-retry.json` has matching baseCommit/headCommit and an `inboundEdgeCandidates` array but `currentFiles` is absent, null, or not an array — e.g. a partially written or hand-trimmed retry file.

Common situations: The retry-writing step crashed after writing inboundEdgeCandidates but before currentFiles; the file was truncated on disk-full; someone edited out currentFiles to 'slim' the file; a schema change renamed the field in one writer but not the validator.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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