affaan-m/ECC · error
Invalid hook entry at
Error message
Invalid hook entry at ${label}.${event}[${index}] What it means
Each entry inside a hook event array must be a JSON object with a non-empty string "id" and an array "hooks". The validator walks every entry by index and throws this error at the first entry violating that shape. It guarantees recorded hook entries can be safely merged into settings without undefined fields downstream.
Solutions
- Open the recorded hooks data and fix the entry at the reported index so it has a non-empty string id and an array hooks field
- Wrap bare hook commands in the expected { id, hooks: [...] } shape
- Regenerate the recording from the canonical hooks.json
- Add a pre-validation pass on your own data to reject entries lacking id/hooks before calling the library
Example fix
// before
{ "hooks": { "type": "command", "command": "node hook.js" } }
// after
{ "id": "format-on-edit", "hooks": [ { "type": "command", "command": "node hook.js" } ] } Defensive patterns
Strategy: validation
Validate before calling
const validEntry = (e) => e && typeof e === 'object' && typeof e.id === 'string' && e.id.length > 0 && Array.isArray(e.hooks);
Type guard
const isHookEntry = (v) => typeof v === 'object' && v !== null && typeof v.id === 'string' && Array.isArray(v.hooks);
Try / catch
try { validateRecordedManagedHooks(hooks); } catch (e) { const m = /hook entry at (.+)\[(\d+)\]/.exec(e.message); if (m) { console.error('Bad entry:', m[1], 'index', m[2]); } throw e; } Prevention
- Always wrap hook commands in { id, hooks: [...] }
- Use a JSON schema validator on hooks.json before install
- Copy entry blocks wholesale, then change id — never delete fields
- Add unit tests for the recording serializer
When it happens
Trigger: Calling validateRecordedManagedHooks (or validate/previousHooks/recordedHooks) where managedHooks[event][index] is not an object, has a missing/empty/non-string id, or has a missing or non-array hooks field.
Common situations: Hand-edited hooks.json entries where "id" was renamed or removed, YAML-to-JSON conversion dropped fields, or a script appended a bare hook command object instead of the wrapper { id, hooks } structure.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid hooks config: expected a JSON object with a hooks…
- Invalid . : expected a non-empty hook array
- Invalid : expected at least one hook event
- Invalid managed hook handler at
- Invalid managed hook handler at
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/e215388ec657908b.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:205
});
});
}
return cloneValue(managedHooks);
}
function validateRecordedManagedHooks(managedHooks, label = 'recorded managed hooks') {
if (!isJsonObject(managedHooks) || Object.keys(managedHooks).length === 0) {
throw new Error(`Invalid ${label}: expected a non-empty JSON object`);
}
for (const [event, entries] of Object.entries(managedHooks)) {
if (!isNonEmptyString(event) || !Array.isArray(entries) || entries.length === 0) {
throw new Error(`Invalid ${label}.${event}: expected a non-empty hook array`);
}
const seenIds = new Set();
entries.forEach((entry, index) => {
if (!isJsonObject(entry) || !isNonEmptyString(entry.id) || !Array.isArray(entry.hooks)) {
throw new Error(`Invalid hook entry at ${label}.${event}[${index}]`);
}
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')) {View on GitHub (pinned to 8321021c54)