affaan-m/ECC · error · Error
Failed to parse at
Error message
Failed to parse ${label} at ${filePath}: ${error.message} What it means
readJsonObject() wraps JSON.parse(fs.readFileSync(...)) for JSON files used as merge payloads during install (hooks config, MCP config, cursor/kimi project scaffolds). When the file exists but is not parseable JSON, the underlying SyntaxError is rethrown with this message, including the file path and the parser's reason.
Solutions
- Reproduce the exact parser error: node -e "JSON.parse(require('fs').readFileSync('<file>','utf8'))" or jq . <file>
- Fix the reported offset - remove trailing commas/comments, close unbalanced braces
- If the file is repo-managed, restore it: git checkout -- <file>
Example fix
// before (hooks config with trailing comma)
{ "hooks": { "PreToolUse": [] }, }
// after
{ "hooks": { "PreToolUse": [] } } Defensive patterns
Strategy: validation
Validate before calling
function assertValidJsonFile(filePath) {
const text = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, ''); // strip BOM
JSON.parse(text); // throws a SyntaxError with the exact offset before the installer runs
}
for (const payloadPath of collectMergePayloadFiles()) {
assertValidJsonFile(payloadPath);
} Try / catch
try {
resolveInstallPlan(options);
} catch (error) {
if (/^Failed to parse .*: /.test(error.message)) {
// error.message names the payload file and parser reason; fix that file, then re-run
} else {
throw error;
}
} Prevention
- Lint every JSON payload in CI (jq . file.json or a JSON.parse check)
- Never put comments, trailing commas, or JSONC syntax in merge-payload files
- Prefer machine-generated configs over hand-edited ones for merge sources
When it happens
Trigger: A merge-json source file containing a trailing comma, unquoted key, JSONC-style comment, or truncated content; a BOM-prefixed file; an empty file handed to JSON.parse.
Common situations: Hand-editing hooks.json or an .mcp.json-style config and leaving a trailing comma; files mangled by git merge conflicts; editors saving comments into .json files.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid at : expected a JSON object
- Missing merge payload for
- Another ECC process is updating Claude settings
- application request must be a JSON object
- At least one guided harness must be selected
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/1b388352e06839b5.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install-executor.js:211
operations.push(
buildCopyFileOperation({
moduleId: options.moduleId,
sourcePath,
sourceRelativePath: options.sourceRelativePath,
destinationPath: options.destinationPath,
strategy: options.strategy || 'preserve-relative-path'
})
);
return true;
}
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 addCursorAgentDataScaffoldOperations(operations, options) {
const scaffoldRoot = path.join(options.sourceRoot, 'scaffolds', 'cursor');
if (!fs.existsSync(scaffoldRoot)) {
return;
}
addFileCopyOperation(operations, {
moduleId: options.moduleId,
sourceRoot: options.sourceRoot,View on GitHub (pinned to 06c5e118c4)