[BUG] AskUserQuestion preview variant bypasses the keybinding registry — option navigation is unbindable

Status Open
Reported on v2.1.223
Maintainer reply None cached
Activity 1 comment · opened Aug 6, 2026

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?

AskUserQuestion renders through two different components, and only one of them goes
through the keybinding system.

Plain variant (no option carries a preview): renders via the shared select
component, which registers its actions with the keybinding hook:

{ "select:next": …, "select:previous": …, "select:accept": …, "select:cancel": … }
// registered with { context: "Select" }

Default Select bindings include j and k, so vim-style navigation works, and
anything a user adds to keybindings.json is honored.

Preview variant (any option carries a preview, giving the side-by-side option
list + preview pane with the press n to add notes footer): registers no
navigation actions. It handles keys with a raw onKeyDown instead:

if (ie.key === "up"   || ie.ctrl && ie.key === "p") { … }        // previous option
else if (ie.key === "down" || ie.ctrl && ie.key === "n") { … }   // next option
else if (ie.key === "return") { … }
else if (ie.key === "n" && !ie.ctrl && !ie.meta) { … }           // add notes
else if (ie.key === "escape") { … }
else if (ie.key >= "1" && ie.key <= "9") { … }

Consequences:

  • j/k do nothing in this dialog, even though they are defaults in the Select

context that the sibling variant uses.

  • No keybindings.json entry can fix it. The component never queries the registry,

so binding select:next, confirm:next, or anything else has no effect.

  • n is hardcoded to the notes field, so it cannot be rebound or freed up.

The same component does register context: "Tabs" for tabs:previous/tabs:next,
so custom tab bindings still work there — only option navigation is unreachable. That
asymmetry within a single component is what makes this look like an oversight rather
than a deliberate choice.

The Notes: text input in this variant is a legitimate reason not to let bare letters
navigate while it is focused, but the existing handler already gates on that state
(if (g) { … return }), so registering the select:* actions for the non-input state
would not conflict.

What Should Happen?

The preview variant should register the same select:next / select:previous /
select:accept / select:cancel actions under context: "Select" that the plain
variant does, so that both the built-in j/k defaults and user-defined bindings work
in both dialogs. n (add notes) should likewise be a bindable action rather than a
hardcoded letter.

Error Messages/Logs

(none — the keys are silently ignored; nothing is written to the debug log)

Steps to Reproduce

No configuration is required — j/k are already defaults in the Select context.

  1. Start claude in an interactive terminal.
  2. Prompt: Use AskUserQuestion to ask me one question with two options. Do not attach previews.
  3. In the dialog, press j and k — the cursor moves between options. Correct.
  4. Prompt: Ask the same question again, but attach a preview string to each option.
  5. The dialog now renders with the side-by-side preview pane. Press j and k

nothing happens. Only / and ctrl+p/ctrl+n move the cursor, and j/k
are swallowed. Pressing n opens the notes field.

  1. Add any binding for this dialog to ~/.claude/keybindings.jsonSelect,

Confirmation, or any other context — and restart. Step 5 is unchanged.

To confirm the config file itself is loading, add:

{ "context": "Tabs", "bindings": { "h": "tabs:previous", "l": "tabs:next" } }

Ask a two-question AskUserQuestion with previews: h/l switches question tabs in
the same dialog where j/k is dead. The file is loaded; the option list just never
consults it.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

(blank)

Claude Code Version

2.1.223 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other (Ghostty, TERM=xterm-256color, TERM_PROGRAM=ghostty)

Additional Information

Regression field: answering honestly, I could not pin this down. The preview variant
appears to be the newer of the two, so questions that previously rendered through the
plain select path — where j/k worked — now render through the hardcoded handler
when the assistant attaches previews. Whether that counts as a regression or as a new
component that never supported bindings depends on which reading you take. The user
experience is that vim navigation in question dialogs stopped working without any
config change.

~/.claude/keybindings.json in use (unchanged since 2026-07-16, all contexts and
actions still valid in 2.1.223):

{
  "$schema": "https://www.schemastore.org/claude-code-keybindings.json",
  "bindings": [
    { "context": "Confirmation", "bindings": { "k": "confirm:previous", "j": "confirm:next" } },
    { "context": "Tabs",         "bindings": { "h": "tabs:previous",   "l": "tabs:next" } },
    { "context": "DiffDialog",   "bindings": { "h": "diff:previousSource", "l": "diff:nextSource" } },
    { "context": "ModelPicker",  "bindings": { "h": "modelPicker:decreaseEffort", "l": "modelPicker:increaseEffort" } },
    { "context": "Attachments",  "bindings": { "h": "attachments:previous", "l": "attachments:next" } },
    { "context": "Footer",       "bindings": { "h": "footer:previous", "j": "footer:down", "k": "footer:up", "l": "footer:next" } }
  ]
}

Related — this is a recurring pattern where individual dialogs implement raw
onKeyDown handlers instead of registering keybinding actions, and each one has to be
reported separately:

  • #40258 (closed) — j/k works in the /resume dialog but not the /mcp dialog
  • #437 (closed) — vim-style navigation missing in configuration and message menus
  • #72476 (open) — mixed feedback report containing "vim mode enabled, but in some

contexts claude forces me to use arrows up and down"; same symptom, no repro

  • #82086 (open) — same preview component, different defect (preview pane truncates

with no way to scroll)

A general fix would be to audit dialogs for raw onKeyDown navigation and route them
through the registry, rather than fixing them one dialog at a time.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗