santifer/career-ops · error
not a regular file
Error message
not a regular file
What it means
verify-ats.mjs checks that its target argument is a regular file via statSync(targetPath).isFile() and throws 'not a regular file' when the stat succeeds but points at a directory, symlink to a directory, FIFO, device, etc. The throw is inside a try block and surfaces to the user as 'ERROR: cannot read target file: ... (not a regular file)'.
Solutions
- Pass the path to a single HTML file, not a directory: node verify-ats.mjs output/cv-001.html
- Check what the path points at: stat <path>
- If auditing a directory, loop over files instead of passing the directory itself
- Remove or replace broken symlinks pointing at non-files
Example fix
// before $ node verify-ats.mjs output/ // after $ node verify-ats.mjs output/cv-tailored.html
Defensive patterns
Strategy: validation
Validate before calling
import { statSync } from 'node:fs';
const st = statSync(targetPath);
if (!st.isFile()) {
console.error(`${targetPath} is not a regular file`);
process.exit(1);
} Type guard
const isRegularFile = (p) => { try { return statSync(p).isFile(); } catch { return false; } }; Try / catch
try {
const html = readFileSync(targetPath, 'utf-8');
} catch (err) {
console.error(`cannot read ${targetPath}: ${err.code || err.message}`);
process.exit(1);
} Prevention
- Pass explicit file paths, never directories, to file-based CLIs
- Quote paths and verify shell globs expanded to exactly one file
- Use tab-completion in the shell to avoid truncated or directory paths
- Check `stat <path>` when unsure what a path points to
When it happens
Trigger: Running the ATS audit against a directory (e.g. `node verify-ats.mjs output/` or `.`), or against a special file, instead of an exported CV HTML file.
Common situations: Passing a directory of generated CVs instead of one file; a broken/symlinked path resolving to a non-file; forgetting the filename at the end of a path; glob not expanded by the shell.
Related errors
- Application Answers section has
- requires a path
- Could not parse existing candidates file at
- ⚠️ Could not save report
- cv.md not found — cannot add to a CV that does not exist
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/5ebb4f3d25e61496.
Report an issue: GitHub.
Appendix: source
Thrown at verify-ats.mjs:851
}
const helpRequested = args.includes('--help') || args.includes('-h');
if (!targetArg || helpRequested) {
console.log(`Usage: node verify-ats.mjs <generated-cv.html> [--keywords "a,b,c"] [--role "..."] [--min-score N] [--json]
Scores a generated CV's HTML for ATS parseability (0-100 + letter grade) and lists
concrete, fixable issues. Deterministic, read-only. Exits 0 when score >= --min-score
(default ${DEFAULT_MIN_SCORE}) and no critical issue is present, else 1.
Keyword coverage (--keywords / --role) is advisory and never changes the score.`);
// An explicit --help is a success; a missing target is a usage error.
process.exit(helpRequested ? 0 : 1);
}
const targetPath = isAbsolute(targetArg) ? targetArg : join(process.cwd(), targetArg);
let html;
try {
if (!statSync(targetPath).isFile()) throw new Error('not a regular file');
html = readFileSync(targetPath, 'utf-8');
} catch (err) {
console.error(`ERROR: cannot read target file: ${targetArg} (${err.code || err.message})`);
process.exit(1);
}
const result = auditAts(html, { keywords, role });
const pass = isPass(result, minScore);
const file = basename(targetPath);
if (asJson) {
console.log(JSON.stringify({ file, pass, minScore, ...result }, null, 2));
} else {
printHuman(result, file, minScore);
}
// Set exitCode (don't process.exit) so buffered stdout drains before exit.
process.exitCode = pass ? 0 : 1;
}
}View on GitHub (pinned to e7abd431fc)