santifer/career-ops · error

⚠️ Could not save report: ${err.message}

Error message

⚠️   Could not save report: ${err.message}

What it means

The report-saving try in openai-eval.mjs failed, so the completed OpenAI-backed evaluation exists only on stdout — no numbered file was written under reports/. The try covers mkdirSync of reports/, reserveReportNumbers(1), and writeFileSync, so the throw can come from the filesystem (EACCES, ENOSPC, EISDIR) or from the reservation allocator. The finally still releases any reserved numbers, keeping numbering consistent. Unlike the Ollama path, a re-run costs API tokens — salvage from stdout first.

Source

Thrown at openai-eval.mjs:424

**Date:** ${today}
**Archetype:** ${archetype}
**Score:** ${score}/5
**Legitimacy:** ${legitimacy}
**PDF:** pending
**Tool:** OpenAI-compatible (${modelName} @ ${endpointHost})

---

${evaluationText.replace(/---SCORE_SUMMARY---[\s\S]*?---END_SUMMARY---/, '').trim()}
`;

    writeFileSync(reportPath, reportContent, 'utf-8');
    console.log(`\n✅  Report saved: reports/${filename}`);

    console.log(`\n📊  Tracker entry (add to data/applications.md):`);
    console.log(`    | ${num} | ${today} | ${company} | ${role} | ${score}/5 | Evaluated | ❌ | [${num}](reports/${filename}) |`);
  } catch (err) {
    console.warn(`⚠️   Could not save report: ${err.message}`);
  } finally {
    if (reservedNumbers.length > 0) {
      try {
        await releaseReportNumbers(reservedNumbers, { reportsDir: PATHS.reports });
      } catch (err) {
        console.warn(`⚠️   Could not release report reservation: ${err.message}`);
      }
    }
  }
}

console.log('\n' + '─'.repeat(66));
console.log(`  Score: ${score}/5  |  Archetype: ${archetype}  |  Legitimacy: ${legitimacy}`);
console.log('─'.repeat(66) + '\n');

console.log(formatBreakdown(tracker, modelName, 'openai'));

View on GitHub (pinned to 60398d6549)

Solutions

  1. Copy the report text from the terminal output above the warning into reports/{num}-{slug}-{date}.md manually — the API result cannot be cheaply regenerated.
  2. Check the basics: ls -ld reports (writable?), df -h (space?), and that no directory shares the target filename.
  3. If parallel workers were running, re-run after they finish — reservation contention is transient.
  4. Pre-flight that reports/ is writable before starting the next paid evaluation.

Example fix

// before
writeFileSync(reportPath, reportContent, 'utf-8');
// after — surface the offending path and code so the loss is diagnosable
try {
  writeFileSync(reportPath, reportContent, 'utf-8');
} catch (err) {
  console.error(`Report write failed at ${reportPath}: ${err.code} — copy the evaluation above into this file manually.`);
  throw err;
}
Defensive patterns

Strategy: validation

Validate before calling

import { accessSync, constants } from 'node:fs';
accessSync('reports', constants.W_OK); // throws EACCES BEFORE the paid API call, not after

Try / catch

try {
  writeFileSync(reportPath, reportContent, 'utf-8');
} catch (err) {
  console.warn(`Could not save report (${err.code}): ${err.message}`);
  // salvage: the full evaluation text is on stdout — tee it to a file; a re-run costs tokens
} finally {
  await releaseReportNumbers(reservedNumbers, { reportsDir }); // always run
}

Prevention

When it happens

Trigger: reports/ exists but is not writable by the current user (EACCES); disk full (ENOSPC); a directory occupies the target filename (EISDIR); reserveReportNumbers throws on sentinel/lock contention with parallel workers — all surface as this same warning.

Common situations: Running as a different user or via cron than the one owning reports/; CI disk quotas; parallel evaluation workers colliding on reservation sentinels; read-only checkouts.

Related errors


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