abhigyanpatwari/GitNexus · critical
Streaming PDG manifest collides with a structural relationsh
Error message
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. What it means
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.
Source
Thrown at gitnexus/src/core/lbug/lbug-adapter.ts:1217
{ err: nodeCopyError },
'[lbug-load] node COPY also failed while relationship emit was failing',
);
}
throw emitErr;
}
const tCsv = mark();
// Merge the streamed PDG-emit per-pair rel CSVs (#2202) into the COPY plan —
// collision-guarded. Done BEFORE node COPY so the serial escape hatch detects a
// manifest/structural pair collision before committing any node rows (legacy
// parity with the pre-overlap path), and the overlap path detects it as early
// as csvResult is available. When a manifest is present, streaming was on and
// the in-memory graph held zero BasicBlocks, so a structural collision means a
// streaming-invariant violation — fail loudly rather than load corrupt data.
if (pdgEmitManifest) {
for (const [pairKey, meta] of pdgEmitManifest.relsByPair) {
if (csvResult.relsByPair.has(pairKey)) {
throw new Error(
`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.`,
);
}
csvResult.relsByPair.set(pairKey, meta);
csvResult.totalValidRels += meta.rows;
}
}
// Serial path: all CSVs are on disk and node COPY has not started — start it
// here so the barrier below blocks on it exactly as the legacy path did.
if (SERIAL) beginNodeCopy(csvResult.nodeFiles);
// FK barrier: node rows must exist before the relationship COPY resolves their
// endpoints. In overlap mode most of node COPY was hidden behind rel emit, so
// this await is the *residual* node-COPY time (≈0 when fully overlapped).
if (nodeCopyPromise) await nodeCopyPromise;
if (nodeCopyError) {View on GitHub (pinned to aac7515d2a)
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
Example fix
// before — PDG edge stored on the real graph (leaks into the structural CSV emit) realGraph.addRelationship(cfgEdge); // after — PDG-typed edges go through the sink's streamed rel writers pdgSink.addRelationship(cfgEdge);
Defensive patterns
Strategy: try-catch
Validate before calling
// before load, assert no PDG-typed edges remain in the in-memory graph:
const PDG_TYPES = new Set(['CFG', 'REACHING_DEF', 'CDG', 'POST_DOMINATE', 'TAINTED', 'SANITIZES']);
let pdgInMemory = 0;
graph.forEachRelationshipFields((_s, _t, type) => {
if (PDG_TYPES.has(type)) pdgInMemory++;
});
if (pdgEmitManifest && pdgInMemory > 0) {
throw new Error(`emit bug: ${pdgInMemory} PDG edge(s) bypassed the streaming sink`);
} Type guard
const isPdgManifestRelCollision = (e: unknown): boolean =>
e instanceof Error &&
e.message.includes('Streaming PDG manifest collides with a structural relationship CSV'); Try / catch
try {
await loadGraphToLbug(graph, repoPath, storagePath, onProgress, manifest);
} catch (e) {
if (isPdgManifestRelCollision(e)) {
// a PDG edge leaked into the structural emit — full rebuild, no retry
await markIndexDirtyAndRebuild(repoPath);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Streaming PDG manifest collides with a structural node CSV f
- PdgEmitSink: ${errors.length} streamed CSV writer(s) hit an
- Connection pool integrity error: expected ${MAX_CONNS_PER_RE
- content filter triggered mid-stream. The generated content w
- LLM returned empty streaming response
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/8bdc23c39563f8ba.
Report an issue: GitHub.