{"record":{"id":"3c886e885f549506","repo":"santifer/career-ops","slug":"could-not-save-html-err-message","errorCode":null,"errorMessage":"⚠️   Could not save HTML: ${err.message}","messagePattern":"⚠️   Could not save HTML: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"openai-tailor.mjs","lineNumber":347,"sourceCode":"  }\n  candidateName = candidateName\n    .toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n\n  const filename = `cv-${candidateName}-${companySlug}.html`;\n  const htmlPath = join(PATHS.output, filename);\n\n  writeFileSync(htmlPath, tailoredHtml, 'utf-8');\n  console.log(`\\n✅  Tailored HTML saved: ${htmlPath}`);\n\n  // Print next steps\n  const pdfFilename = `cv-${candidateName}-${companySlug}-${roleSlug}-${new Date().toISOString().split('T')[0]}.pdf`;\n  const reportNumMatch = reportFilename.match(/^(\\d+)-/);\n  const reportNum = reportNumMatch ? reportNumMatch[1] : '001';\n\n  console.log(`\\n📄  Next step (generate PDF):\\n    node generate-pdf.mjs output/${filename} output/${pdfFilename} --format=letter --report=${reportNum}\\n`);\n\n} catch (err) {\n  console.warn(`⚠️   Could not save HTML: ${err.message}`);\n  process.exit(1);\n}\n","sourceCodeStart":329,"sourceCodeEnd":350,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/openai-tailor.mjs#L329-L350","documentation":"The outer try in openai-tailor.mjs failed — either the mkdirSync/existence check for output/, the writeFileSync of the tailored HTML, or the filename-building code threw — and the process exits with code 1. The tailored CV was NOT saved anywhere. Common fs codes: EACCES (output/ not writable), ENOSPC (disk full), ENOENT (a path component vanished mid-run). Unlike most warnings in this codebase, this one is terminal for the run.","triggerScenarios":"output/ is read-only or owned by another user (EACCES); disk full (ENOSPC); output/ deleted between the existence check and the write; PATHS.output pointing at an existing file rather than a directory.","commonSituations":"CI artifact cleanup racing the run; WSL/Docker permission mismatches; disk exhaustion after generating many CVs; a stray file named output blocking the directory.","solutions":["mkdir -p output && chmod u+w output, then re-run the tailor (the report it read still exists).","Run df -h and free space if the error was ENOSPC.","Verify output is a directory, not a stale file: ls -ld output.","Treat the exit code 1 as 'nothing was written' — never assume partial output."],"exampleFix":"// before\nwriteFileSync(htmlPath, tailoredHtml, 'utf-8');\n} catch (err) {\n  console.warn(`⚠️   Could not save HTML: ${err.message}`);\n  process.exit(1);\n}\n// after — include path and code so the fatal exit is diagnosable\n} catch (err) {\n  console.error(`Could not save HTML to ${htmlPath}: ${err.code ?? err.name} — check output/ permissions and disk space`);\n  process.exit(1);\n}","handlingStrategy":"validation","validationCode":"import { mkdirSync, accessSync, constants } from 'node:fs';\nmkdirSync('output', { recursive: true });\naccessSync('output', constants.W_OK); // fail BEFORE any model call if the target dir cannot take the file","typeGuard":null,"tryCatchPattern":"catch (err) {\n  console.error(`Could not save HTML to ${htmlPath}: ${err.code ?? err.message}`);\n  process.exit(1);\n} // exit non-zero so callers and CI know nothing was produced","preventionTips":["Pre-create and pre-check output/ writability at script start, before any API cost is incurred","Run df -h before long CV-generation sessions","Treat exit code 1 from the tailor as 'nothing was written' — never assume partial output"],"tags":["fs","write","fatal-exit","output-dir","permissions"],"backgroundTag":"file-write-permission-denied","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}