{"record":{"id":"c350e128973c06b5","repo":"Egonex-AI/Understand-Anything","slug":"retry-endpoint-descriptors-are-missing","errorCode":null,"errorMessage":"Retry endpoint descriptors are missing","messagePattern":"Retry endpoint descriptors are missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"understand-anything-plugin/skills/understand/validate-incremental-symbols.mjs","lineNumber":386,"sourceCode":"        }\n      }\n      const result = compareFileSymbols(previous, current, baseEvidence, headEvidence);\n      report.files.push(result);\n      if (result.missing.some(node => node.status !== 'deleted')) report.unresolvedFiles.push(previous.filePath);\n    }\n    report.ok = report.errors.length === 0 && report.unresolvedFiles.length === 0;\n    if (report.ok) {\n      const retryPath = join(intermediateDir, 'incremental-symbol-retry.json');\n      const retry = existsSync(retryPath) ? readJson(retryPath) : null;\n      const candidatePath = join(intermediateDir, 'incremental-edge-candidates.json');\n      const currentCandidates = existsSync(candidatePath) ? readJson(candidatePath) : null;\n      if (currentCandidates && (currentCandidates.baseCommit !== plan.baseCommit\n        || currentCandidates.headCommit !== plan.headCommit || !Array.isArray(currentCandidates.edges))) {\n        throw new Error('Current edge candidates do not match the incremental plan');\n      }\n      const hasRetry = retry?.baseCommit === plan.baseCommit && retry.headCommit === plan.headCommit\n        && Array.isArray(retry.inboundEdgeCandidates);\n      if (hasRetry && !Array.isArray(retry.currentFiles)) throw new Error('Retry endpoint descriptors are missing');\n      const candidates = [\n        ...(currentCandidates?.edges ?? []).map(edge => ({ edge, saved: false })),\n        ...(hasRetry ? retry.inboundEdgeCandidates : []).map(edge => ({ edge, saved: true })),\n      ];\n      if (candidates.length || hasRetry) {\n        const ids = new Set(graph.nodes.map(node => node.id));\n        const replacements = new Map(report.files.flatMap(file => file.replacements)\n          .map(({ oldId, newId }) => [oldId, newId]));\n        const deleted = new Set(report.files.flatMap(file => file.missing.map(node => node.id)));\n        const baselineBindings = new Map(baseline.files.flatMap(file => file.nodes.filter(symbolKind))\n          .map(node => [node.id, deleted.has(node.id) ? null : replacements.get(node.id) ?? node.id]));\n        const currentBindings = new Map();\n        // These descriptors belong to the initial CURRENT analysis, not the\n        // old published graph. Both sides therefore map against HEAD source.\n        for (const previous of hasRetry ? retry.currentFiles : []) {\n          const current = fileGraph(previous.filePath);\n          const needsEvidence = previous.nodes.some(node => symbolKind(node) && !hasPreservedIdentity(node, previous, current, true));\n          const evidence = needsEvidence && previous.filePath ? await parseHead(previous.filePath) : undefined;","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/Egonex-AI/Understand-Anything/blob/07edf82a04371b6f69779b067bdc8a1a8753a9db/understand-anything-plugin/skills/understand/validate-incremental-symbols.mjs#L368-L404","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete `incremental-symbol-retry.json` and re-run the step that produces it so both `inboundEdgeCandidates` and `currentFiles` are written atomically.","Inspect the retry file (`jq 'keys' incremental-symbol-retry.json`) to confirm which fields survived and fix the interrupted writer.","Make the retry writer emit the file in one atomic write (write temp file + rename) to avoid partial records.","If a schema rename caused it, align the producer and validator on the `currentFiles` field name."],"exampleFix":"// before: incomplete retry record\n{ \"baseCommit\": \"a\", \"headCommit\": \"b\", \"inboundEdgeCandidates\": [...] }\n\n// after: regenerate with both fields written atomically\nrm .ua/intermediate/incremental-symbol-retry.json\n# re-run retry recording => { baseCommit, headCommit, inboundEdgeCandidates: [...], currentFiles: [...] }","handlingStrategy":"validation","validationCode":"const rPath = join(dir,'incremental-symbol-retry.json');\nif (existsSync(rPath)) {\n  const r = JSON.parse(readFileSync(rPath,'utf8'));\n  const hasInbound = Array.isArray(r.inboundEdgeCandidates);\n  if (hasInbound && !Array.isArray(r.currentFiles)) {\n    unlinkSync(rPath); // incomplete retry record; force regeneration\n    await rerunRetryRecording();\n  }\n}","typeGuard":"function isCompleteRetryRecord(plan, r) {\n  return r != null && r.baseCommit === plan.baseCommit\n    && r.headCommit === plan.headCommit\n    && Array.isArray(r.inboundEdgeCandidates)\n    && Array.isArray(r.currentFiles);\n}","tryCatchPattern":"try {\n  await validateIncrementalSymbols({ projectRoot, intermediateDir });\n} catch (err) {\n  if (err.message === 'Retry endpoint descriptors are missing') {\n    rmSync(join(intermediateDir, 'incremental-symbol-retry.json'));\n    await rerunRetryRecording(); // rewrite with currentFiles included\n  } else throw err;\n}","preventionTips":["Write retry records atomically (temp file + rename) so partial writes never persist.","Never hand-trim fields (like currentFiles) from intermediate files.","Keep producer and validator field names in sync.","Delete the retry file and regenerate whenever validation flags it as incomplete."],"tags":["retry","incomplete-data","incremental-analysis"],"backgroundTag":"internal-invariant-violation","analyzedSha":"07edf82a04371b6f69779b067bdc8a1a8753a9db","analyzedAt":"2026-09-07T23:20:10.829Z","contentChangedAt":"2026-09-07T23:20:10.829Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}