Claude AskUserQuestion in Headless Mode
Claude AskUserQuestion in Headless Mode
Learned while fixing ModForge's "TOOL ERROR — Answer questions?" in Claude mode (2026-06-15).
The trap
ModForge runs Claude headless: claude --print --input-format stream-json --output-format stream-json (see claude_invocation in src-tauri/src/claude_stream.rs). In that mode there is no built-in UI for AskUserQuestion, so the CLI handles the tool itself:
- Emits a normal
assistant→tool_useblock forAskUserQuestionwith the questions/options ininput({ questions: [{ question, header, multiSelect, options:[{label,description}] }] }, plus acallerfield). - Immediately auto-fails it by emitting a
user→tool_resultwith{ "content": "Answer questions?", "is_error": true }(andtool_use_result: "Error: Answer questions?"). - Claude then treats the question as dismissed, ends the turn, and a
system/post_turn_summaryline reportsstatus_category:"blocked"/needs_action:"…".
Crucially, the CLI does not use the control protocol for this — it never sends a control_request awaiting a control_response. So you cannot "answer the original tool call"; the turn already moved past it.
How ModForge handles it
Mirror the existing permission-denied flow (don't invent a new transport):
- Detect the
AskUserQuestiontool_useblock in the UI and render an answer widget (QuestionPromptinsrc/components/StreamView.tsx). Only the most recent AskUserQuestion stays interactive (lastQuestionToolUseId). - Hide the
"Answer questions?"errortool_result— it's pure headless noise (isAskQuestionErrorResult). - On submit, send the picks back as an ordinary follow-up user message via
claudeStreamSend(onQuestionAnswerinsrc/components/LeafPaneView.tsx). The turn was leftblockedawaiting input, so a plain message resumes it.
This is the same shape as the PermissionPrompt / onPermissionDecision path — a true control-protocol responder is unnecessary because the native --print CLI doesn't delegate either questions or permissions over stdin; it auto-resolves and ModForge reacts to the resulting block.
Related
- [[Claude CLI Session Resume Gotchas]] — other stream-json mode quirks in the same files.