santifer/career-ops · warning

Could not release report reservation: ${e.message}

Error message

Could not release report reservation: ${e.message}

What it means

After writing a report, openrouter-runner.mjs's finally block calls releaseReportNumbers() (imported from reserve-report-num.mjs) to delete the NNN-RESERVED.md sentinel files that claim its report-number slots. If that release throws -- typically ENOENT because the sentinel was already deleted by verify-pipeline's stale GC or another release -- the warn fires and the reservation leaks; leaked sentinels expire on their own after 4 hours.

Source

Thrown at openrouter-runner.mjs:698

    const tsvFile     = `batch/tracker-additions/or-${numStr}-${slug}.tsv`;
    // AGENTS.md: a tracker-addition TSV is a SINGLE data line of 9 tab-separated
    // columns. merge-tracker.mjs reads the whole file as ONE record (no line
    // splitting), so a leading header row makes parts[4]/parts[5] the literal
    // "status"/"score" and the evaluation is skipped ("cannot tell score from
    // status"). Write only the data line.
    writeFile(tsvFile, tsvLine);

    console.log(`\n✅ Report saved: ${relPath}`);
    console.log('\n─── EVALUATION ──────────────────────────────────────\n');
    console.log(result);
    console.log('\n─────────────────────────────────────────────────────\n');

    return relPath;
  } finally {
    try {
      await releaseReportNumbers(reservedNumbers, { reportsDir: path.join(__dirname, 'reports') });
    } catch (e) {
      console.warn(`Could not release report reservation: ${e.message}`);
    }
  }
}

// -- PIPELINE --
async function cmdPipeline(ctx) {
  const pending = readPipeline();
  if (pending.length === 0) {
    console.log('No pending listings in pipeline.md.');
    return;
  }

  console.log(`Processing ${pending.length} pending listing(s) from pipeline.md...\n`);

  for (let i = 0; i < pending.length; i++) {
    const item = pending[i];
    console.log(`\n[${i + 1}/${pending.length}] ${item.company} — ${item.role}`);
    try {

View on GitHub (pinned to 60398d6549)

Solutions

  1. Manually free the range: node reserve-report-num.mjs --release NNN-MMM.
  2. Or run node verify-pipeline.mjs -- sentinels older than 4h are GC'd automatically and reported as 'Removed stale reservation sentinel'.
  3. If release keeps failing on a live sentinel, check permissions on reports/ (ls -la reports/ | grep RESERVED).

Example fix

// before: any release error surfaces as a generic warn
try { await releaseReportNumbers(reservedNumbers, opts); }
catch (e) { console.warn(`Could not release report reservation: ${e.message}`); }
// after: tolerate the benign already-deleted race
try { await releaseReportNumbers(reservedNumbers, opts); }
catch (e) {
  if (!/ENOENT/.test(e.message)) console.warn(`Could not release report reservation: ${e.message}`);
}
Defensive patterns

Strategy: try-catch

Try / catch

// Release in finally, and treat an already-deleted sentinel as success
try {
  await writeReports(reservedNumbers);
} finally {
  try {
    await releaseReportNumbers(reservedNumbers, { reportsDir: REPORTS_DIR });
  } catch (e) {
    if (e.code !== 'ENOENT') throw e; // someone else GC'd/released it: benign
  }
}

Prevention

When it happens

Trigger: A batch run overlapping verify-pipeline.mjs, which unlinks sentinels older than 4h while the worker is still finishing; filesystem errors or permission changes on reports/ between reservation and release; two code paths releasing the same range.

Common situations: Long batch evaluations running concurrently with health checks; interrupted processes on CI; NFS/latency-prone mounts where unlink races are common.

Related errors


AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20). Data as JSON: /api/errors/99e895f9a7e11c81. Report an issue: GitHub.