santifer/career-ops · error · Error

config/profile.yml is empty or invalid YAML: fill it in firs

Error message

config/profile.yml is empty or invalid YAML: fill it in first

What it means

This is the default-path twin of error 304: when the resolved profileSource (config/profile.yml or CAREER_OPS_PROFILE) exists but yaml.load returns null or a non-object, loadProfile throws this message. Like 304, it is immediately caught by the surrounding catch and rethrown as error 308 with the original cause appended, so users usually see the wrapper text.

Source

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

    }
    try {
      const content = readFileSync(customProfilePath, 'utf8');
      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}`);
    }

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Open config/profile.yml and ensure it contains a complete top-level YAML mapping with real values; re-copy from config/profile.example.yml and edit if in doubt.
  2. Validate independently: `node -e "const y=require('js-yaml'),f=require('fs');console.log(y.load(f.readFileSync('config/profile.yml','utf8')))"` and fix whatever prints null or throws.
  3. If the wrapper error (308) appears instead, follow its appended err.message to the specific YAML syntax problem.

Example fix

// before (profile.yml)
---

// after (profile.yml)
name: Ada Lovelace
email: ada@example.com
phone: "+886 900 000 000"
location: Taipei
linkedin: https://www.linkedin.com/in/ada
Defensive patterns

Strategy: type-guard

Validate before calling

import { readFileSync } from 'node:fs';
import yaml from 'js-yaml';
const doc = yaml.load(readFileSync('config/profile.yml', 'utf8'));
if (!doc || typeof doc !== 'object') {
  throw new Error('config/profile.yml is empty or scalar — restore from config/profile.example.yml and edit');
}

Type guard

function isValidProfileDoc(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0;
}

Try / catch

try {
  const profile = loadProfile();
} catch (e) {
  if (e.message.includes('empty or invalid YAML')) {
    console.error('config/profile.yml parsed to nothing — refill it from config/profile.example.yml.');
  } else throw e;
}

Prevention

When it happens

Trigger: config/profile.yml (or the CAREER_OPS_PROFILE target) exists but parses to null/primitive: empty file, comments-only file, bare scalar, lone '---', or YAML that js-yaml rejects and then the catch rewraps.

Common situations: Running `cp config/profile.example.yml config/profile.yml` and forgetting to edit it (template is fine, but truncated/emptied copies happen); a save failure zeroed the file; a botched sed/script edit; a merge conflict left only markers that parse oddly.

Related errors


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