[BUG] Desktop app CCD UserDialogBroker cancels every unknown dialog kind, making AskUserQuestion abort 100% of the time (root cause + proposed fix)

Status Fixed / completed
Reported on v2.1.234
Maintainer reply None cached
Activity 1 comment · opened Aug 19, 2026 · closed Aug 25, 2026

Summary

In Claude Code sessions inside the desktop app (macOS, "CCD" sessions), every AskUserQuestion call fails deterministically: the widget never renders, the model receives [Tool result missing due to internal error], and the UI shows "Tool execution was interrupted."

This is the same symptom as #74797 (reported on Windows, currently mislabeled platform:windows and marked stale) and is adjacent to the Type B "request-drop" cases in #46767. I am filing this separately because the root cause is now traced to a specific function in the shipped app bundle, with a proposed fix. If maintainers prefer, close #74797 as a duplicate of this one (it has the forensic history in its comments).

Root cause

Traced through the shipped binaries (app 1.32885.1 asar, CLI 2.1.234). Every link verified by reading the code; one link inferred and marked as such.

  1. CLI, auto mode: AskUserQuestion goes through the auto-mode classifier, whose every branch for this tool deliberately falls back to asking the user (tengu_auto_mode_fallback_to_ask, reason requires_user_interaction). Working as designed so far.
  1. The ask is not surfaced as a plain can_use_tool permission request. The CLI has a user-dialog registry entry:

``js
kind: "permission_ask_user_question",
payload: (requestId, toolName, permissionResult, questions),
default: {behavior: "cancelled"}
``

so the question rides the request_user_dialog control channel.

  1. The desktop app's CCD UserDialogBroker hard-cancels every dialog kind it does not know, and it only knows one. Verbatim from the extracted chunk (index2.chunk-CUWOzxuz.js):

``js
createOnUserDialog(e) {
return (n, {signal: r}) => {
if (n.dialogKind !==
refusal_fallback_prompt)
return Promise.resolve({behavior:
cancelled});
...
``

No log, no card, no pendingPermissions entry. Instant cancelled.

  1. Cancelled dialog → the tool call aborts → no tool_result is written → orphan tool_use in the session JSONL → the harness synthesizes [Tool result missing due to internal error] → the UI cell renders "Tool execution was interrupted." (This is the inferred link: I cannot see the CLI-side handling of the cancelled response, but it is the only path consistent with the orphan tool_use.)

This accounts for every observation with no leftovers:

  • Fully deterministic (a static !== check), unlike the intermittent result-drops in #46767.
  • Zero Emitted tool permission request lines in main.log for failing calls (this path never reaches handleToolPermission and logs nothing). Working calls always have that line, so it doubles as a reliable dispatch signal.
  • Survives a full app restart (code, not state). Verified.
  • PostToolUse hooks never fire (no tool_result exists), so no client-side fallback can catch it.
  • Cowork works on the same machine, same day (verified in logs: one AskUserQuestion permission request emitted and answered normally in a Cowork session hours after two CCD failures). Its LocalAgentModeSessionManager serves the question through the old can_use_tool → permission card → mcqAnswers flow instead.

It looks like a version-skew regression: the CLI moved AskUserQuestion onto the user-dialog channel, while the app's broker still dates from when refusal_fallback_prompt was the only dialog kind, and it actively cancels instead of declining. Aggravating detail: the CCD spawn passes supportedDialogKinds: ['refusal_fallback_prompt'], so the CLI is told the host cannot service this dialog kind and raises it anyway.

One more inconsistency worth flagging: the desktop system prompt appended to these sessions instructs Claude to use AskUserQuestion "before starting any real work", so the app actively steers the model into the broken path.

Steps to reproduce

  1. Open a Claude Code session inside the Claude desktop app on macOS (spawned with --output-format stream-json --input-format stream-json --permission-prompt-tool stdio --permission-mode auto --allow-dangerously-skip-permissions).
  2. Have the model call AskUserQuestion.
  3. Observe: no widget, [Tool result missing due to internal error] to the model, "Tool execution was interrupted." in the UI, orphan tool_use in the session JSONL, and no Emitted tool permission request ... for AskUserQuestion line in ~/Library/Logs/Claude/main.log.

Reproduces 100% of the time. Historical rate on this machine: 380 AskUserQuestion calls across surfaces, 374 fine; the failures cluster entirely in this surface.

Proposed fix

Either side alone unbreaks users:

App side (sufficient): in UserDialogBroker.createOnUserDialog, stop answering unknown kinds. The SDK layer already has the correct fallback built in; its own log line reads "No onUserDialog handler for request_user_dialog (kind=...) — staying silent so a capable client (or the worker's park deadline) settles it". Returning undefined instead of {behavior:'cancelled'} lets that machinery work. Better still, route permission_ask_user_question to the existing permission-card path (handleToolPermission plus the mcqAnswers mechanism the Cowork manager already uses), which is presumably the intended UX.

CLI side (defense in depth): before raising a request_user_dialog, check the host's declared supportedDialogKinds; if the kind is absent, fall back to the plain can_use_tool permission flow. That protects every host that has not caught up with new dialog kinds and turns this class of bug into graceful degradation instead of a silent abort.

Regression test that would have caught this: spawn a CCD-style session against a host declaring only refusal_fallback_prompt, call AskUserQuestion, and assert that a tool_result exists for the tool_use regardless of outcome. The invariant this bug breaks is "every tool_use gets a tool_result".

Environment

  • macOS 15 (Darwin 25.6.0), Apple Silicon
  • Claude desktop app 1.32885.1, Claude Code CLI 2.1.234
  • Model: Opus 5
  • Not Cowork (Cowork works; that contrast is part of the evidence)

Related

  • #74797 (same bug, original symptom report; forensics in its comments)
  • #46767 (Type B "request-drop" taxonomy this falls under)

View original on GitHub ↗

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