abhigyanpatwari/GitNexus · critical
PdgEmitSink: ${errors.length} streamed CSV writer(s) hit an
Error message
PdgEmitSink: ${errors.length} streamed CSV writer(s) hit an IO error (disk-full / out-of-fds) during the emit — the persisted graph would be truncated, so the run is failed rather than COPYing a partial CSV: ${first instanceof Error ? first.message : String(first)} What it means
Thrown by PdgEmitSink.finalize() when one or more streamed CSV writers recorded a poison error (IO failure) during the PDG (Program Dependence Graph) emit phase. The sink streams node and relationship data to CSV files that are later COPY'd into LadybugDB; if any writer hits disk-full or out-of-file-descriptors mid-stream, the persisted graph would be truncated. Rather than allowing a partial CSV to be loaded (producing a silently incomplete PDG), the entire run is failed. Each CsvWriter stores its error in a poison field checked at close time.
Source
Thrown at gitnexus/src/core/lbug/pdg-emit-sink.ts:239
if (this.bbWriter !== undefined) {
this.bbWriter.close();
if (this.bbWriter.poison !== undefined) errors.push(this.bbWriter.poison);
nodeFiles.set('BasicBlock' as NodeTableName, {
csvPath: this.bbWriter.csvPath,
rows: this.bbWriter.rows,
});
}
const relsByPair = new Map<string, { csvPath: string; rows: number }>();
for (const [pairKey, writer] of this.relWriters) {
writer.close();
if (writer.poison !== undefined) errors.push(writer.poison);
relsByPair.set(pairKey, { csvPath: writer.csvPath, rows: writer.rows });
}
if (errors.length > 0) {
const first = errors[0];
throw new Error(
`PdgEmitSink: ${errors.length} streamed CSV writer(s) hit an IO error ` +
`(disk-full / out-of-fds) during the emit — the persisted graph would ` +
`be truncated, so the run is failed rather than COPYing a partial CSV: ${
first instanceof Error ? first.message : String(first)
}`,
);
}
return { nodeFiles, relsByPair };
}
/**
* Best-effort fd release for the error path — when a language pass throws
* before {@link finalize} runs, the caller's `finally` calls this so the
* BasicBlock + per-pair fds never leak. Idempotent with finalize via the
* `finalized` flag; close errors are swallowed because the run is already
* failing.
*/View on GitHub (pinned to d540b00184)
Solutions
- Free disk space on the volume holding the .gitnexus/ storage directory — PDG CSVs can be several GB for large repos
- Raise the file descriptor limit: `ulimit -n 65536` before running `gitnexus analyze`, or set it system-wide in /etc/security/limits.conf
- Re-run `gitnexus analyze` — the failed PDG emit leaves the dirty flag set, triggering a clean rebuild
- If the repo is extremely large, consider analyzing a subset or increasing available disk to at least 3x the repository size
- Check `df -h` and `ulimit -n` before starting large analyzes to catch these conditions early
Defensive patterns
Strategy: validation
Validate before calling
// Check disk space and file descriptor limit before PDG emit
import { statfs } from 'fs/promises';
async function preflightPdgEmit(outputDir: string): Promise<void> {
const stats = await statfs(outputDir);
const freeGB = (stats.bavail * stats.bsize) / (1024 ** 3);
if (freeGB < 1) {
throw new Error(`Insufficient disk space: ${freeGB.toFixed(2)} GB free — need >= 1 GB for PDG emit`);
}
} Try / catch
try {
const result = await pdgEmitSink.finalize();
} catch (e) {
if (e instanceof Error && e.message.startsWith('PdgEmitSink:')) {
// IO error — check disk space and ulimit, then re-run analyze
logger.error('PDG emit IO failure — check df -h and ulimit -n', e);
}
throw e;
} Prevention
- Check `df -h` and ensure at least 3x the repo size in free disk space before analyzing large repos
- Set `ulimit -n 65536` (or higher) before running analyze on large repos
- In Docker, set `--ulimit nofile=65536:65536` on the container
- Monitor disk usage during long-running analyzes — PDG CSVs can be several GB
When it happens
Trigger: Calling PdgEmitSink.finalize() after writing PDG nodes/edges when the filesystem runs out of space (disk-full) or the process exceeds its file descriptor limit (ulimit -n). The error aggregates all poisoned writers and reports the first one's message. Each relWriters entry (keyed by source-label:target-label pair) and nodeWriter can independently fail.
Common situations: Analyzing a very large repository that generates millions of PDG edges, filling the disk during CSV streaming; running in a CI container with a low ulimit -n (e.g. 1024) where many CSV files exhaust file descriptors; a mounted network filesystem with quota limits; running concurrent analyzes that share a disk partition.
Related errors
- GraphEmitSink: ${errors.length} streamed CSV writer(s) hit a
- short write while staging overlay replacement
- short write while neutralizing {name}
- short write while staging candidate overlay
- sandbox_copy path changed or is unreadable: {relative}: {exc
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/3194a8c70547a272.
Report an issue: GitHub.