santifer/career-ops · error

⚠️ Could not save HTML

Error message

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

What it means

Fatal-path warning in openai-tailor.mjs: the try block that generates and writes the tailored CV HTML to output/ failed, so the script logs err.message and calls process.exit(1). No HTML file is produced and the suggested generate-pdf.mjs next step cannot run.

Solutions

  1. Create the output directory: mkdir -p output
  2. Check the err.message for EACCES/ENOSPC and fix permissions/disk space
  3. Re-run the tailoring command after fixing
  4. Ensure the company slug/name sanitized path is not excessively long

Example fix

// before
catch (err) {
  console.warn(`⚠️   Could not save HTML: ${err.message}`);
  process.exit(1);
}
// after
mkdirSync(PATHS.output, { recursive: true }); // before the write, upstream of the catch
Defensive patterns

Strategy: validation

Validate before calling

import { mkdirSync, accessSync, constants, statfsSync } from 'fs';
mkdirSync(PATHS.output, { recursive: true });
accessSync(PATHS.output, constants.W_OK);
if (statfsSync(PATHS.output).bavail === 0) throw new Error('disk full');

Type guard

function isWritableDir(p) { try { accessSync(p, constants.W_OK); return statSync(p).isDirectory(); } catch { return false; } }

Try / catch

try { writeFileSync(htmlPath, html); } catch (err) { console.warn(`⚠️ Could not save HTML: ${err.message}`); process.exit(1); }

Prevention

When it happens

Trigger: writeFileSync to output/{cv-*.html} fails: output/ directory missing, EACCES/EPERM on the file, ENOSPC, or the path derived from candidateName/companySlug is invalid.

Common situations: Fresh clone without output/ (gitignored and not auto-created); read-only CI workspace; disk full; extremely long company slug exceeding filename limits.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/3c886e885f549506. Report an issue: GitHub.

Appendix: source

Thrown at openai-tailor.mjs:349

  }
  candidateName = candidateName
    .toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');

  const filename = `cv-${candidateName}-${companySlug}.html`;
  const htmlPath = join(PATHS.output, filename);

  writeFileSync(htmlPath, tailoredHtml, 'utf-8');
  console.log(`\n✅  Tailored HTML saved: ${htmlPath}`);

  // Print next steps
  const pdfFilename = `cv-${candidateName}-${companySlug}-${roleSlug}-${new Date().toISOString().split('T')[0]}.pdf`;
  const reportNumMatch = reportFilename.match(/^(\d+)-/);
  const reportNum = reportNumMatch ? reportNumMatch[1] : '001';

  console.log(`\n📄  Next step (generate PDF):\n    node generate-pdf.mjs output/${filename} output/${pdfFilename} --format=letter --report=${reportNum}\n`);

} catch (err) {
  console.warn(`⚠️   Could not save HTML: ${err.message}`);
  process.exit(1);
}

View on GitHub (pinned to aac998c7ed)