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

  1. Pass the path to a single HTML file, not a directory: node verify-ats.mjs output/cv-001.html
  2. Check what the path points at: stat <path>
  3. If auditing a directory, loop over files instead of passing the directory itself
  4. 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

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


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)