{"record":{"id":"8bdc23c39563f8ba","repo":"abhigyanpatwari/GitNexus","slug":"streaming-pdg-manifest-collides-with-a-structural-8bdc23","errorCode":null,"errorMessage":"Streaming PDG manifest collides with a structural relationship CSV for pair \"${pairKey}\" — a PDG edge leaked into the in-memory graph during a streamed emit.","messagePattern":"Streaming PDG manifest collides with a structural relationship CSV for pair \"(.+?)\" — a PDG edge leaked into the in-memory graph during a streamed emit\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"gitnexus/src/core/lbug/lbug-adapter.ts","lineNumber":1217,"sourceCode":"        { err: nodeCopyError },\n        '[lbug-load] node COPY also failed while relationship emit was failing',\n      );\n    }\n    throw emitErr;\n  }\n  const tCsv = mark();\n\n  // Merge the streamed PDG-emit per-pair rel CSVs (#2202) into the COPY plan —\n  // collision-guarded. Done BEFORE node COPY so the serial escape hatch detects a\n  // manifest/structural pair collision before committing any node rows (legacy\n  // parity with the pre-overlap path), and the overlap path detects it as early\n  // as csvResult is available. When a manifest is present, streaming was on and\n  // the in-memory graph held zero BasicBlocks, so a structural collision means a\n  // streaming-invariant violation — fail loudly rather than load corrupt data.\n  if (pdgEmitManifest) {\n    for (const [pairKey, meta] of pdgEmitManifest.relsByPair) {\n      if (csvResult.relsByPair.has(pairKey)) {\n        throw new Error(\n          `Streaming PDG manifest collides with a structural relationship CSV for pair ` +\n            `\"${pairKey}\" — a PDG edge leaked into the in-memory graph during a streamed emit.`,\n        );\n      }\n      csvResult.relsByPair.set(pairKey, meta);\n      csvResult.totalValidRels += meta.rows;\n    }\n  }\n\n  // Serial path: all CSVs are on disk and node COPY has not started — start it\n  // here so the barrier below blocks on it exactly as the legacy path did.\n  if (SERIAL) beginNodeCopy(csvResult.nodeFiles);\n\n  // FK barrier: node rows must exist before the relationship COPY resolves their\n  // endpoints. In overlap mode most of node COPY was hidden behind rel emit, so\n  // this await is the *residual* node-COPY time (≈0 when fully overlapped).\n  if (nodeCopyPromise) await nodeCopyPromise;\n  if (nodeCopyError) {","sourceCodeStart":1199,"sourceCodeEnd":1235,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/core/lbug/lbug-adapter.ts#L1199-L1235","documentation":"Rel-side twin of the node collision guard, in loadGraphToLbug (gitnexus/src/core/lbug/lbug-adapter.ts:1217). During a streamed PDG emit, PDG edges (CFG / REACHING_DEF / CDG / POST_DOMINATE / TAINTED / SANITIZES) go to per-pair CSV writers and the in-memory graph holds none. Before node COPY commits, each manifest pair (e.g. BasicBlock|BasicBlock) is merged into the COPY plan; a pair that already exists in the structural csvResult means a PDG edge leaked into the in-memory graph. The run aborts rather than load corrupt/duplicated edge data.","triggerScenarios":"loadGraphToLbug called with a pdgEmitManifest whose relsByPair contains a pairKey that streamAllCSVsToDisk also emitted — caused by a PDG-typed relationship added to the real graph instead of the sink (a new PDG edge type missing from PDG_EDGE_TYPES, or an emit path calling real.addRelationship directly).","commonSituations":"Adding a new PDG relationship type without registering it in PdgEmitSink's PDG_EDGE_TYPES; forks that store PDG edges in memory for read-back during streamed runs; version upgrades that reorder emit passes.","solutions":["Re-run the analyze — the dirty flag forces a full rebuild on the next run","If reproducible, report a GitNexus bug noting the colliding pair key from the message","When adding a PDG edge type, add it to PDG_EDGE_TYPES in pdg-emit-sink.ts so it streams instead of leaking into the structural emit","Verify no emit code path calls real.addRelationship for a PDG-typed edge during streamed runs"],"exampleFix":"// before — PDG edge stored on the real graph (leaks into the structural CSV emit)\nrealGraph.addRelationship(cfgEdge);\n\n// after — PDG-typed edges go through the sink's streamed rel writers\npdgSink.addRelationship(cfgEdge);","handlingStrategy":"try-catch","validationCode":"// before load, assert no PDG-typed edges remain in the in-memory graph:\nconst PDG_TYPES = new Set(['CFG', 'REACHING_DEF', 'CDG', 'POST_DOMINATE', 'TAINTED', 'SANITIZES']);\nlet pdgInMemory = 0;\ngraph.forEachRelationshipFields((_s, _t, type) => {\n  if (PDG_TYPES.has(type)) pdgInMemory++;\n});\nif (pdgEmitManifest && pdgInMemory > 0) {\n  throw new Error(`emit bug: ${pdgInMemory} PDG edge(s) bypassed the streaming sink`);\n}","typeGuard":"const isPdgManifestRelCollision = (e: unknown): boolean =>\n  e instanceof Error &&\n  e.message.includes('Streaming PDG manifest collides with a structural relationship CSV');","tryCatchPattern":"try {\n  await loadGraphToLbug(graph, repoPath, storagePath, onProgress, manifest);\n} catch (e) {\n  if (isPdgManifestRelCollision(e)) {\n    // a PDG edge leaked into the structural emit — full rebuild, no retry\n    await markIndexDirtyAndRebuild(repoPath);\n  }\n  throw e;\n}","preventionTips":["Route PDG-typed relationship writes through the sink during streamed runs","Keep PDG_EDGE_TYPES in pdg-emit-sink.ts synchronized with the set of PDG relationship types","Assert in tests that a streamed run leaves zero BasicBlock nodes and zero PDG edges in the real graph"],"tags":["gitnexus","pdg","streaming","invariant-violation","csv","edges"],"backgroundTag":"double-write-collision","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}