santifer/career-ops · error · Error

is not valid YAML ( ) — refusing to overwrite it. Fix the…

Error message

${file} is not valid YAML (${String(err.message).split('
')[0]}) — refusing to overwrite it. Fix the file and re-run; nothing was changed.

What it means

parsePluginConfig loads config/plugins.yml before writing enable/disable flags into it. If the file has content but yaml.load fails, it throws rather than proceeding — the library refuses to overwrite a file it cannot parse, since doing so would silently destroy the user's config. Nothing is modified when this fires.

Solutions

  1. Open config/plugins.yml and fix the YAML syntax — the error includes the parser's first error line to locate it
  2. Validate the file with a YAML linter or node -e "require('yaml').load(require('fs').readFileSync('config/plugins.yml','utf8'))"
  3. Replace tabs with spaces; ensure consistent 2-space indentation and quoted strings where needed
  4. Re-run the plugins command after the file parses cleanly

Example fix

// before (broken YAML: tab indentation)
plugins:
	my-plugin:
		enabled: true
// Error: config/plugins.yml is not valid YAML (bad indentation of a mapping entry) ...
// after
plugins:
  my-plugin:
    enabled: true
Defensive patterns

Strategy: validation

Validate before calling

import { parseDocument } from 'yaml';
const doc = parseDocument(readFileSync('config/plugins.yml', 'utf8'));
if (doc.errors.length) throw new Error('plugins.yml has YAML errors: ' + doc.errors[0]);

Type guard

null

Try / catch

try { await pluginsCmd.enable('my-plugin'); } catch (e) { if (e.message.includes('is not valid YAML')) { console.error(e.message); /* surface parser line, do not auto-rewrite */ } throw e; }

Prevention

When it happens

Trigger: Calling the plugin enable/disable write path (e.g. plugins.mjs set/enable/disable) while config/plugins.yml contains malformed YAML — bad indentation, tabs, unclosed quotes, stray characters.

Common situations: Hand-editing plugins.yml with a tab character; merging config changes that broke indentation; copy-pasting YAML from docs with smart quotes; a truncated write from a crashed editor.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/e161d2306deef653. Report an issue: GitHub.

Appendix: source

Thrown at plugins.mjs:243

  // Absent is not unreadable: no file means a first enable, which is the whole
  // point of the function. Only an existing-but-unparseable file is a refusal.
  if (raw == null) return {};
  // An empty or comment-only file is "no config yet", not a corrupt one, and it
  // must take the same path as an absent one. Decided from the text rather than
  // from what the parser does with it: js-yaml 4 returns undefined for an empty
  // document and js-yaml 5 throws "expected a document, but the input is empty",
  // so inferring it from the parser would make this refuse to write over a
  // comment-only config on one major and not the other.
  const hasContent = raw.split('\n').some((line) => {
    const t = line.trim();
    return t !== '' && !t.startsWith('#');
  });
  if (!hasContent) return {};
  let cfg;
  try {
    cfg = yaml.load(raw);
  } catch (err) {
    throw new Error(
      `${file} is not valid YAML (${String(err.message).split('\n')[0]}) — refusing to overwrite it. `
      + 'Fix the file and re-run; nothing was changed.',
    );
  }
  if (cfg == null) return {};   // empty file: same as absent
  // A scalar or a list parses cleanly and is still not a config. Spreading one
  // below would discard it just as silently as the empty object did.
  if (typeof cfg !== 'object' || Array.isArray(cfg)) {
    throw new Error(`${file} does not contain a YAML mapping — refusing to overwrite it. Nothing was changed.`);
  }
  return cfg;
}

// Write enabled:true/false into config/plugins.yml, merging (never clobbering
// the user's other plugins or non-secret settings).
function setEnabled(id, on, settings) {
  const file = path.join(ROOT, 'config', 'plugins.yml');
  const cfg = parsePluginConfig(existsSync(file) ? readFileSync(file, 'utf8') : null, file);

View on GitHub (pinned to aac998c7ed)