[BUG] keybindings.json accepts unknown action names silently, and the bad binding kills the key instead of falling back to the default
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
keybindings.json validation checks that an action is a string, but never checks that the string is a known action. A typo'd action name is accepted, stored, and resolves at dispatch time to an action that has no handler. The key then does nothing at all, and because the bad binding still wins its context, it also suppresses whatever would otherwise have handled that key.
Worse, an unknown action overrides the default binding for that key in that context, since user blocks are appended after defaults and last-wins per context+key. So a typo does not degrade to the default, it silently kills the key.
There is no user-visible signal. The validator emits nothing for unknown actions, and even the issues it does detect (Unknown context "X") only reach the debug log, which nobody reads unless they already suspect their config.
Three real examples from one hand-written config, all silently dead until I disassembled the binary to check them:
{ "context": "Global", "bindings": { "ctrl+shift+o": "app:toggleTeammatePreview" } }
{ "context": "Autocomplete", "bindings": { "enter": "autocomplet:accept" } }
{ "context": "Settings", "bindings": { "enter": "settings:close" } }
{ "context": "Doctor", "bindings": { "f": "doctor:fix" } }
app:toggleTeammatePreviewdoes not exist in 2.1.220 (no such string anywhere in the executable). The key is inert.autocomplet:acceptis a typo forautocomplete:accept. BecauseAutocompleteis one of the three contexts that callregisterActiveContext, it outranksChat, soenterresolved to a nonexistent action and the slash-command menu could not be accepted with Enter. Enter did not even fall through to itsChataction.settings:closedoes not exist. The real default isenter->select:accept, so this line disabled Enter inside/config.Doctoris not a valid context anddoctor:fixis not a valid action. This one at least produces a validation error, but only in the debug log.
What Should Happen?
- An unknown action name should be a validation error, listed alongside the existing
Unknown context "X"error, with a "did you mean" suggestion. The full action list is already embedded in the binary and already used to render the docs, so the check is a Set lookup. - A binding that fails validation should be dropped, so the key falls back to its default rather than being killed.
- Validation issues should be surfaced somewhere a user will see them, for example a one-line notice on startup when
keybindings.jsonhas errors, or a section in/doctor. Debug-log-only means these bugs are undiscoverable in practice.
Steps to Reproduce
- Write
~/.claude/keybindings.json:
{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"bindings": [
{ "context": "Settings", "bindings": { "enter": "settings:close" } }
]
}
- Start
claude, run/config, move to any row, press Enter.
Expected: either the row is accepted (invalid binding dropped, default select:accept applies), or a startup warning tells you settings:close is not a valid action.
Actual: nothing happens. Enter is dead in /config and no message is ever shown.
Is this a regression?
I don't know
Claude Code Version
2.1.220 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
The relevant validation code path emits invalid_action only for typeof action !== "string" && action !== null, plus separate checks for command: naming, voice:pushToTalk on bare letters, duplicate keys, and reserved chords. The known-action list exists in the same bundle (the array beginning "app:interrupt","app:exit",..., 134 entries in 2.1.220) and is what the docs table is generated from, so unknown-action detection is a small addition to an existing pass rather than new machinery.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗