affaan-m/ECC · error

Failed to parse at

Error message

Failed to parse ${label} at ${filePath}: ${error.message}

What it means

readJsonObject reads a JSON file from disk and parses it. If fs.readFileSync succeeds but JSON.parse fails (malformed JSON, trailing commas, comments, truncated file, wrong encoding), the parse error is wrapped in this error naming the label and file path plus the underlying parse message. It separates parse failures from shape failures (error 364).

Solutions

  1. Fix the JSON syntax in the file at filePath — validate it with a JSON linter or `JSON.parse` in a REPL; the wrapped message names the exact parse offset.
  2. Restore the file from version control or regenerate it with the scaffolder.
  3. Check for a UTF-8 BOM or wrong encoding; re-save the file as plain UTF-8.
  4. If the file was truncated by a crashed process, rerun the generator to rewrite it.

Example fix

// before: settings.json with trailing comma
{ "locale": "en", }

// after
{ "locale": "en" }
Defensive patterns

Strategy: validation

Validate before calling

function isParseableJsonObject(p) {
  try { JSON.parse(fs.readFileSync(p, 'utf8').replace(/^\uFEFF/, '')); return true; }
  catch { return false; }
}

Type guard

function parsesAsObject(text) {
  try { const v = JSON.parse(text); return v !== null && typeof v === 'object' && !Array.isArray(v); }
  catch { return false; }
}

Try / catch

try {
  const cfg = readJsonObject(path, 'scaffold config');
} catch (e) {
  if (e.message.startsWith('Failed to parse')) {
    console.error(`Malformed JSON in ${path}; validate with a JSON linter and fix syntax.`);
    return restoreFromVcs(path);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling readJsonObject(filePath, label) — e.g. materializeScaffoldOperation reading a scaffold/manifest JSON — when the file content is not valid JSON: syntax errors, BOM/encoding issues, truncated writes, or the file being HTML/error output instead of JSON.

Common situations: Hand-editing a scaffold or settings JSON and leaving a trailing comma or comment; a partial write from a crashed process; downloading a 404 HTML page saved as .json; smart quotes introduced by a rich-text editor.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/cface571f034809a. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/install/plan.js:124

  return {
    kind: 'copy-file',
    moduleId,
    sourcePath,
    sourceRelativePath,
    destinationPath,
    strategy,
    ownership: 'managed',
    scaffoldOnly: false,
    ...(contentTransform ? { contentTransform } : {}),
  };
}

function readJsonObject(filePath, label) {
  let parsed;
  try {
    parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch (error) {
    throw new Error(`Failed to parse ${label} at ${filePath}: ${error.message}`);
  }

  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error(`Invalid ${label} at ${filePath}: expected a JSON object`);
  }

  return parsed;
}

function materializeClaudeSettingsOperation(sourceRoot, operation) {
  const sourcePath = path.join(sourceRoot, operation.sourceRelativePath);
  if (!fs.existsSync(sourcePath)) {
    return [];
  }

  // Stable ids and descriptions live in hooks/hooks.metadata.json; readHooksConfig
  // merges them back so managed settings entries keep their ids.
  const hooksConfig = readHooksConfig(sourcePath, operation.sourceRelativePath);

View on GitHub (pinned to 8321021c54)