Describe the bug
If a hook config (~/.copilot/config.json hooks, .github/hooks/*.json, or a plugin's hooks.json) has an event key that isn't one of the recognized camelCase names, the CLI silently drops it. The hook never registers and never fires, and at normal log levels nothing tells you why.
The easiest way to hit this is casing: the docs use camelCase (preToolUse, postToolUse, …), but it's easy to write PreToolUse (PascalCase, like Claude Code's hook events) or make a typo. When that happens the config still loads "successfully" and the bad key is quietly ignored.
In the bundled app.js (1.0.63) the loader validates keys against a hardcoded set:
Wir = new Set(["sessionStart","sessionEnd","userPromptSubmitted","preToolUse",
"postToolUse","errorOccurred","agentStop","subagentStop","preCompact"])
The hooks schema is .passthrough(), so an unknown key passes validation instead of erroring — and the only place it's reported is gated behind debug mode:
if (e?.debug) {
const l = Object.keys(config.hooks).filter(c => !Wir.has(c));
if (l.length > 0) e.debug(`Ignoring unknown hook event(s) in ${file}: ${l.join(", ")}`);
}
Worth noting: the loader's else branch already calls e.error(...) for schema-invalid configs. Unknown keys are the one config mistake that escapes that visible path, purely because .passthrough() routes them around it. The dispatcher then only ever reads the exact camelCase keys, so a mis-cased key never fires.
I ran into this while building agent-connector — an SDK that deploys one MCP server + lifecycle hooks across many agent CLIs from a single definition — and spent a while puzzled that my Copilot hooks never ran, with nothing in the normal output to explain it. The fix on my end was a one-character casing change.
Affected version
GitHub Copilot CLI 1.0.63
Steps to reproduce the behavior
- Create
.github/hooks/test.json with a PascalCase event key:
{ "version": 1, "hooks": { "PreToolUse": [ { "type": "command", "bash": "echo fired >> /tmp/hook.log" } ] } }
- Start a session and run a bash command.
- The hook never fires,
/tmp/hook.log is never written, and normal output says nothing. The only trace is at --log-level debug: Ignoring unknown hook event(s) in <file>: PreToolUse.
- Change the key to
preToolUse — it works.
Expected behavior
An unrecognized hook event key should surface a visible warning at normal log level (naming the bad key and file, ideally hinting the closest valid name), the same way an otherwise-invalid config already does — not just a debug-only line. The unknown-key diff is already computed for the debug log, so it's mostly a matter of routing it to a visible warning. A silently non-firing hook is hard to diagnose, especially since there's currently no way to list registered hooks to confirm what loaded (#3871).
Additional context
Describe the bug
If a hook config (
~/.copilot/config.jsonhooks,.github/hooks/*.json, or a plugin'shooks.json) has an event key that isn't one of the recognized camelCase names, the CLI silently drops it. The hook never registers and never fires, and at normal log levels nothing tells you why.The easiest way to hit this is casing: the docs use camelCase (
preToolUse,postToolUse, …), but it's easy to writePreToolUse(PascalCase, like Claude Code's hook events) or make a typo. When that happens the config still loads "successfully" and the bad key is quietly ignored.In the bundled
app.js(1.0.63) the loader validates keys against a hardcoded set:The
hooksschema is.passthrough(), so an unknown key passes validation instead of erroring — and the only place it's reported is gated behind debug mode:Worth noting: the loader's
elsebranch already callse.error(...)for schema-invalid configs. Unknown keys are the one config mistake that escapes that visible path, purely because.passthrough()routes them around it. The dispatcher then only ever reads the exact camelCase keys, so a mis-cased key never fires.I ran into this while building agent-connector — an SDK that deploys one MCP server + lifecycle hooks across many agent CLIs from a single definition — and spent a while puzzled that my Copilot hooks never ran, with nothing in the normal output to explain it. The fix on my end was a one-character casing change.
Affected version
GitHub Copilot CLI 1.0.63Steps to reproduce the behavior
.github/hooks/test.jsonwith a PascalCase event key:{ "version": 1, "hooks": { "PreToolUse": [ { "type": "command", "bash": "echo fired >> /tmp/hook.log" } ] } }/tmp/hook.logis never written, and normal output says nothing. The only trace is at--log-level debug:Ignoring unknown hook event(s) in <file>: PreToolUse.preToolUse— it works.Expected behavior
An unrecognized hook event key should surface a visible warning at normal log level (naming the bad key and file, ideally hinting the closest valid name), the same way an otherwise-invalid config already does — not just a debug-only line. The unknown-key diff is already computed for the debug log, so it's mostly a matter of routing it to a visible warning. A silently non-firing hook is hard to diagnose, especially since there's currently no way to list registered hooks to confirm what loaded (#3871).
Additional context
copilot mcp list, hooks have no equivalent #3871 (no way to list installed hooks).