affaan-m/ECC · error
Invalid managed hook entry at
Error message
Invalid managed hook entry at ${label}.${event}[${index}]: invalid matcher What it means
When an entry includes a matcher property, its value must be either a string or a plain JSON object. validateManagedHooks throws this when matcher is present but of another type (number, boolean, array, null). This validates the matcher shape without interpreting its contents, keeping ECC compatible with both string and object matcher forms Claude Code supports.
Solutions
- Set matcher to a string pattern (e.g. 'Bash', 'Write|Edit', '*') or a plain object form like { tool: 'Bash' } where supported.
- Check the value type with typeof m === 'string' || isPlainObject(m) before calling.
- Locate the entry via the index in the message.
- When generating from data, coerce the matcher with String(m) if it should be textual.
Example fix
// before
{ id: 'lint', matcher: 123, hooks: [...] }
// after
{ id: 'lint', matcher: 'Bash', hooks: [...] } Defensive patterns
Strategy: type-guard
Validate before calling
for (const entries of Object.values(managedHooks)) {
entries.forEach((e, i) => {
if ('matcher' in e && typeof e.matcher !== 'string'
&& (e.matcher === null || typeof e.matcher !== 'object' || Array.isArray(e.matcher))) {
throw new Error(`Entry ${i} matcher must be a string or object`);
}
});
} Type guard
function isValidMatcher(m) {
return typeof m === 'string'
|| (m !== null && typeof m === 'object' && !Array.isArray(m)
&& (Object.getPrototypeOf(m) === Object.prototype || Object.getPrototypeOf(m) === null));
} Try / catch
try {
validateManagedHooks(hooks);
} catch (err) {
if (/invalid matcher/.test(err.message)) {
console.error('matcher must be a string pattern or a plain object — coerce with String(m) if needed');
}
throw err;
} Prevention
- Quote matcher values in YAML/JSON so they are strings, not booleans/numbers (e.g. matcher: "Yes" pitfall)
- Convert RegExp objects to .source or .toString() before use
- Validate matcher type in your config loader
- Avoid passing null — omit the key for matcher-less events instead
When it happens
Trigger: Passing matcher: 42, matcher: true, matcher: ['Bash'], or matcher: null on an entry; a template/serialization bug emitting a numeric matcher.
Common situations: Programmatic matcher generation that yields non-string values; YAML/JSON editing that turns "Bash" into an unquoted value parsed as boolean/number (e.g. matcher: Yes); a regex literal accidentally passed as a RegExp object instead of a string pattern.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Invalid . : expected an array
- Invalid managed hook entry at
- Invalid managed hook handler at
- Canonical session snapshot must be an object
- Canonical session snapshot requires
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/6569b0829b6690b1.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/install/claude-settings.js:176
}
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) => {
validateHookHandler(hook, `${label}.${event}[${index}].hooks[${hookIndex}]`);
});
});
}
return cloneValue(managedHooks);
}
function validateRecordedManagedHooks(managedHooks, label = 'recorded managed hooks') {View on GitHub (pinned to 8321021c54)