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

  1. Free disk space on the volume holding the .gitnexus/ storage directory — PDG CSVs can be several GB for large repos
  2. Raise the file descriptor limit: `ulimit -n 65536` before running `gitnexus analyze`, or set it system-wide in /etc/security/limits.conf
  3. Re-run `gitnexus analyze` — the failed PDG emit leaves the dirty flag set, triggering a clean rebuild
  4. If the repo is extremely large, consider analyzing a subset or increasing available disk to at least 3x the repository size
  5. 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

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


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/3194a8c70547a272. Report an issue: GitHub.