affaan-m/ECC · error
Invalid : expected "hooks" to be a JSON object
Error message
Invalid ${label}: expected "hooks" to be a JSON object What it means
When a Claude settings object contains a "hooks" key, its value must itself be a JSON object keyed by hook event names. This error is thrown when hooks exists but is a scalar, array, or null. The library only manages hook maps, so any other shape is rejected.
Solutions
- Change settings.hooks to an object mapping event names to arrays
- Remove the hooks key entirely if hooks are not used
- If hooks were disabled by setting null, use a different mechanism (e.g. remove managed entries) instead
- Validate the merged settings JSON with a schema before writing
Example fix
// before
{ "hooks": null }
// after
{ "hooks": { "PreToolUse": [] } } Defensive patterns
Strategy: type-guard
Validate before calling
if ('hooks' in settings && (settings.hooks === null || typeof settings.hooks !== 'object' || Array.isArray(settings.hooks))) throw new Error('hooks must be an object'); Type guard
const isHooksMap = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
Try / catch
try { return parseSettings(s); } catch (e) { if (e.message.includes('"hooks" to be a JSON object')) { const { hooks, ...rest } = s; return parseSettings(rest); } throw e; } Prevention
- Never set hooks to null/true to disable hooks — remove the key
- Validate merged settings against the Claude settings schema
- Use object spreads when merging hook maps, not replacement
- Keep a known-good settings.json as reference
When it happens
Trigger: Calling parseSettings/validatedSettings/validateSettings with a settings object where settings.hooks is not an object — e.g. "hooks": true, "hooks": [], or "hooks": null.
Common situations: Hand-merged settings where a hooks list overwrote the hooks map, a tool writing "hooks": null to 'disable' hooks, or schema drift from an older Claude settings format.
Related errors
- Invalid : expected hooks. to be an array
- Claude settings contains multiple hooks for event
- Invalid Claude hooks preference
- Invalid hook entry at
- INVALID_HOOK_MODE
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/1420200d0f4b84c3.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:225
if (seenIds.has(entry.id)) {
throw new Error(
`Invalid ${label}: expected unique id "${entry.id}" within event "${event}"`
);
}
seenIds.add(entry.id);
});
}
return cloneValue(managedHooks);
}
function validateSettings(settings, label = 'Claude settings') {
if (!isJsonObject(settings)) {
throw new Error(`Invalid ${label}: expected a JSON object`);
}
if (Object.prototype.hasOwnProperty.call(settings, 'hooks')) {
if (!isJsonObject(settings.hooks)) {
throw new Error(`Invalid ${label}: expected "hooks" to be a JSON object`);
}
for (const [event, entries] of Object.entries(settings.hooks)) {
if (!Array.isArray(entries)) {
throw new Error(`Invalid ${label}: expected hooks.${event} to be an array`);
}
}
}
return cloneValue(settings);
}
function replacePluginRootPlaceholders(value, pluginRoot) {
if (typeof pluginRoot !== 'string') {
throw new Error('Invalid Claude plugin root: expected a string');
}
if (typeof value === 'string') {
return value.split(PLUGIN_ROOT_PLACEHOLDER).join(pluginRoot);
}View on GitHub (pinned to 8321021c54)