affaan-m/ECC · error · Error

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

Error message

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

What it means

Thrown by readJsonObject in scripts/lib/install/apply.js when JSON.parse fails. This local copy of the helper is used for three labels: 'hooks config' (source hooks.json read in buildResolvedClaudeHooks), 'existing JSON config' (destination .mcp.json / mcp.json being merged into during a merge-json operation), and 'MCP config' (source MCP file on the copy-file-with-filtering path). The original parse error is inlined.

Source

Thrown at scripts/lib/install/apply.js:49

  }
  const mappings = [];
  for (const operation of plan.operations) {
    if (operation.kind === 'copy-file' && operation.sourceRelativePath) {
      mappings.push({
        sourceRel: operation.sourceRelativePath,
        destRel: path.relative(plan.targetRoot, operation.destinationPath),
      });
    }
  }
  return buildInstallIndex(mappings);
}

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 stateWithContentDigests(state) {
  return {
    ...state,
    operations: (state.operations || []).map(operation => {
      if (
        !operation.destinationPath
        || !fs.existsSync(operation.destinationPath)
        || !fs.statSync(operation.destinationPath).isFile()
      ) {

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Read the message — it names the exact label and absolute filePath that failed to parse.
  2. Run node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))" <filePath> to surface the position.
  3. Fix the JSON syntax error, or back up and recreate the file, then re-run the install.
  4. Strip any BOM: sed -i '1s/^\xEF\xBB\xBF//' <filePath>.

Example fix

// before (existing ~/.claude/.mcp.json)
{
  "mcpServers": {
    "x": { "command": "npx", }
  }
}

// after
{
  "mcpServers": {
    "x": { "command": "npx" }
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

function preflightJson(filePath) {
  let raw;
  try { raw = fs.readFileSync(filePath, 'utf8'); } catch (e) { if (e.code !== 'ENOENT') throw e; return; }
  try { JSON.parse(raw.replace(/^\uFEFF/, '')); }
  catch (e) { throw new Error(`Preflight fail ${filePath}: ${e.message}`); }
}
['.mcp.json', 'mcp.json', 'hooks/hooks.json'].forEach(f => {
  preflightJson(path.join(targetRoot, f));
});

Type guard

function isJsonObject(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}

Try / catch

try {
  applyInstallPlan(plan);
} catch (err) {
  if (/Failed to parse .* at /.test(err.message)) {
    console.error('Corrupt JSON file — see path in message:', err.message);
  }
  throw err;
}

Prevention

When it happens

Trigger: During applyInstallPlan: an existing destination .mcp.json being merged into is corrupt; the source hooks.json is corrupt; a source MCP file that will be copied is corrupt.

Common situations: User's existing ~/.claude/.mcp.json has a syntax error from a prior manual edit; a partial write left by a crashed previous run; an editor inserted a BOM or trailing comma.

Understand the failure class

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/84810658853fb2d3. Report an issue: GitHub.