affaan-m/ECC · error
Invalid managed hook entry at
Error message
Invalid managed hook entry at ${label}.${event}[${index}]: missing matcher What it means
Entries for events that support tool matchers (all events except UserPromptSubmit, Notification, Stop, SubagentStop — see EVENTS_WITHOUT_MATCHER) must include a matcher property, even if it is an empty string. validateManagedHooks throws this when the matcher key is absent on an entry for a matcher-requiring event. Matcher presence is required so the managed block remains deterministic and diffable.
Solutions
- Add a matcher to the entry — a string like 'Bash' or '*' (or an object form, see the invalid-matcher error).
- Confirm the event actually requires a matcher; if it is UserPromptSubmit/Notification/Stop/SubagentStop, matcher is optional and you can omit it safely.
- Check the index in the message (label.event[index]) to find the offending entry.
- Use JSON.stringify with explicit matcher field when serializing to avoid dropped keys.
Example fix
// before
{ id: 'lint', hooks: [{ type: 'command', command: 'lint' }] } // under PreToolUse
// after
{ id: 'lint', matcher: 'Bash', hooks: [{ type: 'command', command: 'lint' }] } Defensive patterns
Strategy: validation
Validate before calling
const NO_MATCHER = new Set(['UserPromptSubmit', 'Notification', 'Stop', 'SubagentStop']);
for (const [event, entries] of Object.entries(managedHooks)) {
if (NO_MATCHER.has(event)) continue;
entries.forEach((e, i) => {
if (!Object.prototype.hasOwnProperty.call(e, 'matcher')) {
throw new Error(`${event}[${i}] requires a matcher`);
}
});
} Type guard
function entriesHaveMatchers(hooks) {
const NO_MATCHER = new Set(['UserPromptSubmit', 'Notification', 'Stop', 'SubagentStop']);
return Object.entries(hooks).every(([event, entries]) => NO_MATCHER.has(event)
|| entries.every(e => 'matcher' in e));
} Try / catch
try {
inspectManagedOperation(hooks);
} catch (err) {
if (/missing matcher/.test(err.message)) {
console.error('Add a matcher (string like "Bash" or "*") to this entry, or move it to a matcher-less event');
}
throw err;
} Prevention
- Remember matcher is optional ONLY for UserPromptSubmit/Notification/Stop/SubagentStop
- Use '*' when you want to match all tools
- Default matcher: '*' in your entry factory so the field is always present
- Copy entry shapes from the same event family, not across events
When it happens
Trigger: Passing an entry without a matcher key for a matcher-requiring event, e.g. { PreToolUse: [{ id: 'x', hooks: [...] }] }; conversely this does NOT fire for Stop or UserPromptSubmit entries without matcher.
Common situations: Copying an entry shape from a matcher-less event (like Stop) to PreToolUse; writing entries from memory without matcher; destructuring/serializing an entry and dropping undefined matcher fields.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Invalid managed hook entry at
- Canonical session snapshot requires session.sourceTarget to…
- Canonical session snapshot requires session to be an object
- Canonical session snapshot requires workers to be an array
- clip has no 'path
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/5481724aaf9aacbb.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:167
throw new Error(
`Invalid managed hook entry at ${label}.${event}[${index}]: expected a JSON object`
);
}
if (typeof entry.id !== 'string' || entry.id.trim() === '') {
throw new Error(
`Invalid managed hook entry at ${label}.${event}[${index}]: `
+ 'expected a non-empty unique id'
);
}
if (seenIds.has(entry.id)) {
throw new Error(`Invalid ${label}: expected globally unique id "${entry.id}"`);
}
seenIds.add(entry.id);
if (
!Object.prototype.hasOwnProperty.call(entry, 'matcher')
&& !EVENTS_WITHOUT_MATCHER.has(event)
) {
throw new Error(
`Invalid managed hook entry at ${label}.${event}[${index}]: missing matcher`
);
}
if (
Object.prototype.hasOwnProperty.call(entry, 'matcher')
&& typeof entry.matcher !== 'string'
&& !isJsonObject(entry.matcher)
) {
throw new Error(
`Invalid managed hook entry at ${label}.${event}[${index}]: invalid matcher`
);
}
if (!Array.isArray(entry.hooks) || entry.hooks.length === 0) {
throw new Error(
`Invalid managed hook entry at ${label}.${event}[${index}]: expected hooks`
);
}
entry.hooks.forEach((hook, hookIndex) => {View on GitHub (pinned to 8321021c54)