Keybinding set to null consumes keystroke instead of passing through
Issue Description
When setting a keybinding to null in ~/.claude/keybindings.json to unbind it, the keystroke is still consumed rather than being passed through to the host application.
Environment
- Claude Code Version: 2.1.29
- Platform: macOS (Darwin 25.2.0)
- Context: Running via IntelliJ IDEA plugin with Emacs keybindings
Problem Details
I want to use Ctrl+B for Emacs-style backward-character movement. I've configured keybindings.json to:
- Unbind Ctrl+B by setting it to
null - Rebind the background task feature to Alt+B
{
"bindings": [
{
"context": "Global",
"bindings": {
"ctrl+b": null
}
},
{
"context": "Task",
"bindings": {
"task:background": "alt+b"
}
}
]
}
Expected Behavior
Setting "ctrl+b": null should cause Claude Code to not handle the Ctrl+B keystroke at all, allowing it to pass through to:
- The terminal's readline (in CLI mode)
- The IDE's keymap (in IDEA plugin mode)
Actual Behavior
- Ctrl+B no longer triggers the "background task" action (good)
- Ctrl+B does nothing - it is consumed/swallowed (bad)
- The keystroke never reaches IDEA's Emacs keybindings for cursor movement
Impact
Users who rely on Emacs keybindings (Ctrl+A, Ctrl+B, Ctrl+E, Ctrl+F, etc.) cannot reclaim Ctrl+B for cursor navigation even after unbinding it in Claude Code.
Suggested Fix
When a keybinding is set to null, Claude Code should not register a handler for that keystroke at all, allowing it to propagate to the underlying input system.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
haha... came here to share the bug report claude wrote for _me_ guess we're not the only ones...
----
Unbinding keys in keybindings.json swallows keystrokes instead of passing them through to text input (breaks emacs-style cursor movement)
Description
When a default keybinding is unbound by setting it to
nullin~/.claude/keybindings.json, the keystroke is consumed/dropped rather than being passed through to the underlying text input component. This makes it impossible to reclaim standard emacs/readline cursor movement keys (Ctrl+A, Ctrl+E, Ctrl+F, Ctrl+B, etc.) in contexts where Claude Code binds them to app-level actions.Steps to reproduce
~/.claude/keybindings.jsonwith the following content:``
json
``{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"bindings": [
{
"context": "Confirmation",
"bindings": {
"ctrl+e": null
}
}
]
}
Ctrl+EExpected behavior
Ctrl+Eshould move the cursor to the end of the line, as it does in standard readline/emacs-style text input (and as it works in the Chat input context).Actual behavior
Ctrl+Edoes nothing. The keystroke is consumed by the keybinding system rather than being passed through to the text input component. Before unbinding,Ctrl+Etriggeredconfirm:toggleExplanation— so the unbinding does remove the action, but the key event is swallowed instead of falling through to the input layer.Broader impact
This affects any emacs/readline keybinding that conflicts with a default app action in any context:
| Key | Default action that blocks readline | Context |
|---|---|---|
|
Ctrl+E|confirm:toggleExplanation| Confirmation ||
Ctrl+E|transcript:toggleShowAll| Transcript ||
Ctrl+B|task:background| Task |There is no workaround because the keybindings system has no cursor movement actions — it only exposes app-level actions. So you can unbind a key, but you cannot rebind it to cursor movement. Users who rely on emacs-style editing (Ctrl+A/E for line start/end, Ctrl+F/B for char movement, Alt+F/B for word movement) lose these keys in any context where Claude Code has a default binding on them.
Suggested fix
When a keystroke is unbound (set to
null) and no other binding matches, the key event should be passed through to the underlying text input component rather than being consumed. This would let standard readline/emacs cursor movement work naturally whenever the app-level binding is removed.Alternatively (or additionally), expose cursor movement actions (e.g.
input:cursorLineEnd,input:cursorLineStart,input:cursorForwardWord, etc.) so users can explicitly bind keys to text editing behavior.Environment
Confirming for
Enterkey,Chatcontext, on Linux + WezTerm.Environment
Description
When
Enterkey is unbound in~/.claude/keybindings.json, it becomes completely blocked instead of passing through and inserting new line.Expected behavior
When
Enterkey is unbound in~/.claude/keybindings.json, key presses should insert new lines.Steps to reproduce
``
json
``{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"$docs": "https://code.claude.com/docs/en/keybindings",
"bindings": [
{
"context": "Chat",
"bindings": {
"enter": null
}
}
]
}
EnterkeyPressing
Enterkey has no effect in Claude Code.Additional data point: Claude Code's input widget already supports emacs-style keybindings natively.
C-f(forward char),C-k(kill line),M-b/M-f(word movement) all work as expected in the Chat input.C-bis the exception — it's intercepted by the keybinding layer fortask:backgroundbefore reaching the input widget. Setting"ctrl+b": nullstops the action but still swallows the keystroke, so it never reaches the input component that would handle it correctly.This confirms that passthrough is the right fix: the input widget already knows what to do with
C-b, it just never receives it.Environment: Claude Code 2.1.45, Linux, standalone terminal (vterm/Emacs).
— Valentin & Claude