santifer/career-ops · error · Error
Failed to read CV file at
Error message
Failed to read CV file at ${customCvPath}: ${err.message} What it means
Thrown by loadProfile() when an explicit customCvPath exists but reading or parsing it fails — readFileSync error or an exception from parseCvMarkdown(). The raw cause is wrapped into a single message naming the path.
Solutions
- Read the wrapped err.message for the root cause (EACCES, EISDIR, or the parseCvMarkdown error text).
- Confirm the file is plain-text UTF-8 markdown with the standard sections (Summary, Experience, Education, Skills).
- Check permissions and that the path is a regular file (not a directory or symlink to nothing).
- Restore cv.md structure per the project's convention (standard markdown headings) or re-export the CV.
Example fix
# before: cv.md is a binary PDF renamed to .md $ file cv.md # PDF document # after: keep the real markdown at cv.md, or convert first # pandoc cv.pdf -o cv.md
Defensive patterns
Strategy: try-catch
Validate before calling
import { statSync, readFileSync } from 'node:fs';
if (!statSync(cvPath).isFile()) throw new Error(`${cvPath} is not a regular file`);
readFileSync(cvPath, 'utf8'); // fails fast on permission/encoding problems Try / catch
try {
const profile = loadProfile(null, cvPath);
} catch (err) {
if (err.message.includes(`Failed to read CV file at ${cvPath}`)) {
console.error(`CV unreadable: ${err.message}`);
const profile = loadProfile(null); // degrade gracefully without CV data
} else throw err;
} Prevention
- Keep cv.md as plain UTF-8 markdown with standard sections (Summary, Experience, Education, Skills).
- Never rename binary files (PDFs) to .md; convert them properly before use.
- Check file permissions before running export scripts, especially in containers/CI.
When it happens
Trigger: loadProfile(profilePath, customCvPath) where the file exists but is unreadable (permissions, directory) or parseCvMarkdown throws on malformed/unexpected markdown structure.
Common situations: CV file with permissions too restrictive; path is actually a directory; CV markdown restructured so a section the parser expects (headings, bullet format) is missing or malformed; binary file (e.g. a PDF) saved as .md.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- - advisory phrase
- ⚠️ config/profile.yml cv.sections lists only
- ⚠️ config/profile.yml cv.sections lists
- ⚠️ config/profile.yml cv.sections lists
- ⚠️ config/profile.yml cv.sections lists
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/3a4b2d5771c2e2e3.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/export-ats-text.mjs:257
if (!profileRaw || typeof profileRaw !== 'object') {
throw new Error('config/profile.yml is empty or invalid YAML: fill it in first');
}
} catch (err) {
throw new Error(`Failed to read profile file at ${profileSource}: ${err.message}`);
}
}
let cvData = {};
const cvSource = customCvPath || process.env.CAREER_OPS_CV || 'cv.md';
if (customCvPath) {
if (!existsSync(customCvPath)) {
throw new Error(`CV file not found at: ${customCvPath}`);
}
try {
const cvText = readFileSync(customCvPath, 'utf8');
cvData = parseCvMarkdown(cvText);
} catch (err) {
throw new Error(`Failed to read CV file at ${customCvPath}: ${err.message}`);
}
} else if (existsSync(cvSource)) {
try {
const cvText = readFileSync(cvSource, 'utf8');
cvData = parseCvMarkdown(cvText);
} catch {
// cv.md is optional if profile supplies necessary fields
}
}
return normalizeProfile(profileRaw, cvData);
}
/**
* Formats profile object into structured plain-text section blocks.
*/
export function formatAtsText(profile = {}, options = {}) {
const normalized = normalizeProfile(profile);View on GitHub (pinned to aac998c7ed)