affaan-m/ECC · error
Failed to parse
Error message
Failed to parse ${label}: ${error.message} What it means
parseSettings wraps JSON.parse of raw Claude settings text and rethrows any SyntaxError with a labeled message identifying which settings file failed to parse. The original parse error is preserved as the `cause` for diagnostics. This guards callers (readSettings, readSettingsSnapshot, runTests) from opaque JSON syntax errors with no file context.
Solutions
- Open the settings file and fix the JSON syntax; the `cause` property and error message list the exact parse position.
- Validate the file with `node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))" <path>` before rerunning.
- If hand-edits keep breaking, restore the file from version control or let the installer regenerate it.
- If the file is JSONC (with comments), strip comments before storing as settings.json.
Example fix
// before (settings.json)
{
"hooks": {...},
}
// after (remove trailing comma)
{
"hooks": {...}
} Defensive patterns
Strategy: try-catch
Validate before calling
const raw = fs.readFileSync(settingsPath, 'utf8').replace(/^\uFEFF/, '');
try { JSON.parse(raw); } catch (e) { throw new Error(`${settingsPath} is not valid JSON: ${e.message}`); } Type guard
function isParseableJson(text) { try { JSON.parse(text); return true; } catch { return false; } } Try / catch
try {
const settings = readSettings(path);
} catch (error) {
if (error.message.startsWith('Failed to parse')) {
console.error(`Fix JSON in ${path}:`, error.cause?.message);
return;
}
throw error;
} Prevention
- Never hand-edit settings.json without validating JSON afterward
- Use a JSON linter/formatter on ~/.claude files
- Keep settings files in git so corruption is recoverable
- Avoid JSONC features (comments, trailing commas) in settings files
When it happens
Trigger: Calling readSettings(path) or readSettingsSnapshot(path) when the file at settingsPath contains malformed JSON (trailing commas, truncated file, BOM, comments in JSONC format), or passing a non-JSON string directly to parseSettings.
Common situations: A user hand-edited ~/.claude/settings.json and left a trailing comma or comment; an interrupted write left the file truncated; the settings file was saved as JSONC by an editor expecting comments.
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
- Cannot merge ECC configuration into invalid JSON at
- Invalid JSON in at
- Invalid JSON in
- candidate configuration exceeds 1 MiB
- Cannot merge ECC configuration at
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/262c7447c3fdf5d6.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:315
function materializeManagedHooks(hooksConfig, targetRoot) {
if (!isJsonObject(hooksConfig) || !isJsonObject(hooksConfig.hooks)) {
throw new Error('Invalid hooks config: expected a JSON object with a hooks object');
}
if (!isNonEmptyString(targetRoot)) {
throw new Error('Invalid Claude target root: expected a non-empty string');
}
return validateManagedHooks(resolveManagedHookCommands(
replacePluginRootPlaceholders(hooksConfig.hooks, targetRoot),
targetRoot
));
}
function parseSettings(rawSettings, label = 'Claude settings') {
let settings;
try {
settings = JSON.parse(rawSettings);
} catch (error) {
throw new Error(`Failed to parse ${label}: ${error.message}`, { cause: error });
}
return validateSettings(settings, label);
}
function readSettings(settingsPath, fileSystem = fs) {
const reader = fileSystem && fileSystem.fs ? fileSystem.fs : fileSystem;
let rawSettings;
try {
rawSettings = reader.readFileSync(settingsPath, 'utf8');
} catch (error) {
if (error && error.code === 'ENOENT') {
return {};
}
throw error;
}
return parseSettings(rawSettings, `Claude settings at ${settingsPath}`);
}
View on GitHub (pinned to 8321021c54)