santifer/career-ops · error · Error

Failed to read profile file at ${profileSource}: ${err.messa

Error message

Failed to read profile file at ${profileSource}: ${err.message}

What it means

The catch around the default-path profile read wraps every failure — fs errors on config/profile.yml/CAREER_OPS_PROFILE, YAML syntax exceptions, and the nested error 307 — into this single message with the path and the underlying err.message. It guarantees callers of the no-argument path always get one predictable error shape for an unloadable profile.

Source

Thrown at scripts/export-ats-text.mjs:243

      profileRaw = yaml.load(content);
      if (!profileRaw || typeof profileRaw !== 'object') {
        throw new Error(`Invalid YAML in profile file: ${customProfilePath}`);
      }
    } catch (err) {
      throw new Error(`Failed to read profile file at ${customProfilePath}: ${err.message}`);
    }
  } else {
    if (!existsSync(profileSource)) {
      throw new Error('config/profile.yml not found: fill it in first');
    }
    try {
      const content = readFileSync(profileSource, 'utf8');
      profileRaw = yaml.load(content);
      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');

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Inspect the appended err.message (e.g. 'bad indentation of a mapping entry', 'EACCES') and fix that specific cause.
  2. Fix YAML formatting: spaces not tabs, quote strings containing special characters, keep one top-level mapping.
  3. Restore a known-good file: `cp config/profile.example.yml config/profile.yml`, edit values, rerun.
  4. Verify access: `ls -l config/profile.yml` and ensure it is a readable regular file owned by the running user.

Example fix

// before (unquoted colon breaks YAML)
title: Senior: Backend Engineer

// after
title: "Senior: Backend Engineer"
Defensive patterns

Strategy: try-catch

Validate before calling

import { statSync } from 'node:fs';
import yaml from 'js-yaml';
const src = process.env.CAREER_OPS_PROFILE || 'config/profile.yml';
const st = statSync(src); // real fs error if unreadable/dir
yaml.load(readFileSync(src, 'utf8')); // real syntax error if malformed

Try / catch

try {
  const profile = loadProfile();
} catch (e) {
  if (e.message.startsWith('Failed to read profile file at')) {
    const cause = e.message.split(': ').slice(1).join(': ');
    if (cause.includes('EACCES')) console.error('Fix permissions on config/profile.yml');
    else if (cause.includes('ENOENT')) console.error('File vanished mid-run — recreate it');
    else console.error('YAML syntax problem:', cause);
  } else throw e;
}

Prevention

When it happens

Trigger: existsSync passes but readFileSync then fails (permissions changed, file deleted in between, EISDIR), or yaml.load throws on malformed YAML, or the nested 'empty or invalid YAML' error fires — all rethrown here.

Common situations: config/profile.yml with tab indentation (js-yaml's classic failure); unquoted values containing ':' or '#'; CAREER_OPS_PROFILE pointing at a directory; unreadable file after chmod; Windows line-ending/BOM issues confusing the parser in edge setups.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/4f58cf9e7f1fba94. Report an issue: GitHub.