{"record":{"id":"38f4499a967e878a","repo":"JuliusBrussee/caveman","slug":"cave-runtime-segment-id-collision","errorCode":"cave_runtime_segment_id_collision","errorMessage":"cave_runtime_segment_id_collision","messagePattern":"cave_runtime_segment_id_collision","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/agent/src/context-ir.ts","lineNumber":287,"sourceCode":"\n  return { ir: { schemaVersion: 1, segments }, bodies };\n}\n\nexport function appendRuntimeContextSegment(\n  lowered: LoweredContext,\n  segment: RuntimeContextSegment,\n): ContextSegment {\n  const digest = sha256(segment.body);\n  const existing = lowered.ir.segments.find((item) => item.id === segment.id);\n  if (existing) {\n    // Segment identity is content-derived, so the same id must\n    // carry the same bytes. If it does not, the id has been reused for\n    // different content — the exact confusion that once let compaction\n    // substitute an OLD message's compressed body into a NEW message. Fail\n    // closed rather than returning the stale segment and poisoning the CCR\n    // restore map.\n    if (existing.provenanceDigest !== digest) {\n      throw new Error(\"cave_runtime_segment_id_collision\");\n    }\n    return existing;\n  }\n  const bodyHandle = `cave_local_sha256:${digest}`;\n  (lowered.bodies as Map<string, Uint8Array>).set(bodyHandle, segment.body.slice());\n  const appended: ContextSegment = {\n    id: segment.id,\n    kind: segment.kind,\n    stability: \"turn\",\n    safety: \"S4\",\n    priority: \"normal\",\n    recovery: \"exact_ccr\",\n    cacheRegion: \"live_zone\",\n    privacy: \"local_sensitive\",\n    opaque: opaquePayload(segment.body),\n    provenanceDigest: digest,\n    tokenCount: estimateTokens(segment.body),\n    bodyHandle,","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/context-ir.ts#L269-L305","documentation":"When a runtime segment is lowered into the Context IR, segment identity is content-derived: if a segment id already exists, it must carry the same sha256 digest. The same id with different bytes means the id was reused for new content — exactly the confusion that once let compaction substitute an OLD message's compressed body into a NEW message — so the library fails closed instead of poisoning the CCR restore map.","triggerScenarios":"Appending a segment whose id matches an existing one while its body changed: sequentially generated ids like `tool-result-1` reused after content updates, or a retry that regenerates output under the same id within one context build.","commonSituations":"Custom id schemes that aren't content-derived; caching layers that reuse keys across content versions; mutating a segment body after it was already lowered into the IR.","solutions":["Derive ids from content, e.g. include the digest: `id: 'tool-result-' + digest.slice(0, 16)`.","Never mutate a segment's body after adding it; add a new segment with a new id instead.","Start a fresh lowered context when content identity changes rather than reusing ids."],"exampleFix":"// before\nconst id = `tool-result-${index}`; // same id, new bytes on retry\n// after\nconst digest = sha256(body);\nconst id = `tool-result-${digest.slice(0, 16)}`; // id changes with content","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await withContextSegments(segments); // lowering/addition of runtime segments\n} catch (error) {\n  if (error instanceof Error && error.message === \"cave_runtime_segment_id_collision\") {\n    throw new Error(\n      \"segment id reused for different content: regenerate ids from the content digest\",\n    );\n  }\n  throw error;\n}","preventionTips":["Derive segment ids from content (include the sha256 digest) so a body change changes the id.","Treat segment bodies as immutable once lowered; add a new segment instead of editing one."],"tags":["integrity","id-collision","context-ir","fail-closed"],"backgroundTag":"duplicate-id-content-mismatch","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}