AskUserQuestion silently returns empty answers when called inside plugin skills

Status Fixed / completed
Reported on v2.1.63
Maintainer reply ✓ Yes — blois
Activity 12 comments · opened Feb 28, 2026 · closed Mar 2, 2026
💡 Likely answer: A maintainer (blois, collaborator) responded on this thread — see the highlighted reply below.

Bug Description

AskUserQuestion silently auto-completes with empty answers when called inside a skill loaded from a plugin via the Skill tool. The user never sees the interactive prompt UI. The same AskUserQuestion call works perfectly outside the skill context in the same session.

Environment

  • Claude Code version: 2.1.63
  • OS: Linux (WSL2) 6.6.87.2-microsoft-standard-WSL2
  • Permission mode: --dangerously-skip-permissions

Steps to Reproduce

  1. Start Claude Code with --dangerously-skip-permissions
  1. Install a plugin that contains a skill using AskUserQuestion. For example, from Roxabi/roxabi-plugins:

``
claude plugin marketplace add Roxabi/roxabi-plugins
claude plugin install compress
``

  1. The skill declares AskUserQuestion in its allowed-tools:

``yaml
---
name: compress
allowed-tools: Read, Write, Edit, Glob, Grep, AskUserQuestion
---
``

  1. Invoke the skill: /compress:compress some-file
  1. When the skill calls AskUserQuestion, the user is never prompted. The tool returns immediately with an empty answer:

``
User has answered your questions: . You can now continue with the user's answers in mind.
``

  1. In the same session, call AskUserQuestion directly (outside any skill context) — it works correctly, shows the prompt UI, and returns the user's selection.

Reproduction with multiple skills

Reproduced with two different skills from the same plugin repo (compress and 1b1), both declaring AskUserQuestion in allowed-tools. The behavior is consistent — AskUserQuestion never renders inside a plugin skill context.

Expected Behavior

AskUserQuestion should display the interactive prompt UI and wait for user input when called inside a skill, even in --dangerously-skip-permissions mode.

Actual Behavior

AskUserQuestion returns immediately with empty/no answers. The user never sees the prompt. The skill proceeds as if the user responded, but with no actual input.

Root Cause

We traced through the minified v2.1.63 source and found the exact code path.

The permission evaluator (Xv9) has an early return that bypasses requiresUserInteraction()

async function Xv9(tool, input, ...) {
  let appState = await toolUseContext.getAppState();

  // STEP 1: Check alwaysAllowRules (includes skill allowed-tools via "command" source)
  let allowMatch = $0B(appState.toolPermissionContext, tool);
  if (allowMatch) {
    return { behavior: "allow", updatedInput: input, ... };
    // ^^^ RETURNS HERE — everything below is skipped
  }

  // STEP 2: Check deny rules
  // STEP 3: Check ask rules

  // STEP 4: Call tool's own checkPermissions()
  let permResult = await tool.checkPermissions(input, toolUseContext);

  // STEP 5: requiresUserInteraction guard — NEVER REACHED when in skill context
  if (tool.requiresUserInteraction?.() && permResult?.behavior === "ask") {
    return permResult;  // This would force the UI prompt
  }

  // STEP 6: bypassPermissions mode, etc.
}

How the skill triggers the bug

  1. The Skill tool's contextModifier injects the skill's allowed-tools into alwaysAllowRules.command:

``javascript
contextModifier(context) {
// Wraps getAppState() to inject allowedTools into:
// toolPermissionContext.alwaysAllowRules.command
// This includes "AskUserQuestion" from the skill's allowed-tools
}
``

  1. When AskUserQuestion is called within the skill, Xv9 runs:
  • $0B collects rules from all sources including "command" (via c2H/clA)
  • llA matches: rule.ruleValue.toolName === "AskUserQuestion"true
  • Returns { behavior: "allow" } immediately with the original input (empty answers: {})
  • requiresUserInteraction() at Step 5 is never reached
  • User never sees the prompt
  1. The AskUserQuestion tool receives the auto-approved input with empty answers and returns:

``
"User has answered your questions: ."
``

Why it works outside skill context

Without an active skill, alwaysAllowRules.command doesn't contain "AskUserQuestion". Step 1 doesn't match, so the flow reaches Steps 4–5 where checkPermissions() returns "ask" and requiresUserInteraction() preserves it → user gets prompted.

The hook path already handles this correctly

The hook approval path has the proper guard:

if (hookApproved && !tool.requiresUserInteraction?.()) → skip permission check
if (hookApproved && tool.requiresUserInteraction?.())  → still run permission check

The alwaysAllowRules path in Xv9 lacks the same guard.

Suggested Fix

Add a requiresUserInteraction() check to the alwaysAllowRules early return in Xv9:

let allowMatch = $0B(appState.toolPermissionContext, tool);
if (allowMatch && !tool.requiresUserInteraction?.()) {
  return { behavior: "allow", updatedInput: input, ... };
}

This matches the pattern already used in the hook approval path and ensures AskUserQuestion (and any future tools declaring requiresUserInteraction) always reaches the UI prompt regardless of how the permission was granted.

Related Issues

  • #9846 — Original bug with same symptom in --dangerously-skip-permissions mode. Fixed in v2.0.28 by adding the requiresUserInteraction() guard at Step 5. That fix works for direct calls but doesn't cover the alwaysAllowRules early return at Step 1.
  • #14956 — Broader allowed-tools issues

Plugin repo for reproduction

https://github.com/Roxabi/roxabi-plugins — both compress and 1b1 skills trigger this bug.

View original on GitHub ↗

12 Comments

github-actions[bot] · 6 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/29530
  2. https://github.com/anthropics/claude-code/issues/9846
  3. https://github.com/anthropics/claude-code/issues/29360

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

monsterhxw · 6 months ago

I have the same issue.

MickaelV0 · 6 months ago

Workaround

Removing AskUserQuestion from the skill's allowed-tools frontmatter prevents it from being injected into alwaysAllowRules.command, so the tool goes through the normal permission path where requiresUserInteraction() is checked and the UI prompt renders correctly.

Commit: https://github.com/Roxabi/roxabi-plugins/commit/3cabe10

monsterhxw · 6 months ago
## Workaround Removing AskUserQuestion from the skill's allowed-tools frontmatter prevents it from being injected into alwaysAllowRules.command, so the tool goes through the normal permission path where requiresUserInteraction() is checked and the UI prompt renders correctly. Commit:  Roxabi/roxabi-plugins@3cabe10

I'm using the same workaround you mentioned.

terrylica · 6 months ago

Root Cause: PreToolUse Hooks with Wildcard Matchers Corrupt AskUserQuestion Input

This is the same underlying bug as #29530. I've tracked it down and published a fix.

Why It Happens

If you have any PreToolUse hook with:

  1. A wildcard/empty matcher (matches all tools), and
  2. The hook returns updatedInput with extra properties (e.g., environment variables)

...then AskUserQuestion's input schema gets corrupted. Claude Code replaces the tool input wholesale with the hook's updatedInput, and the extra keys (env, etc.) cause the interactive UI to silently fail.

Why "Inside Plugin Skills" Specifically

The workaround in #29547 comment (removing AskUserQuestion from allowed-tools) works because it changes the permission path. When AskUserQuestion is in allowed-tools, it goes through alwaysAllowRules → PreToolUse hooks fire → input gets corrupted. When it's NOT in allowed-tools, it hits the normal permission check where requiresUserInteraction() bypasses hooks.

But this is a workaround, not a fix — the real issue is that hooks shouldn't corrupt tool input.

The Fix (For Hook Authors)

In your PreToolUse hook, skip UI tools entirely — don't return updatedInput for them:

const UI_TOOLS = new Set([
  "AskUserQuestion", "EnterPlanMode", "ExitPlanMode",
  "TaskCreate", "TaskUpdate", "TaskList", "TaskGet",
  "Read", "Glob", "Grep", "WebSearch", "WebFetch", "Agent", "Skill",
]);

// In your hook:
if (UI_TOOLS.has(tool_name)) {
  // Allow without mutation — do NOT return updatedInput
  console.log(JSON.stringify({
    hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow" }
  }));
  return;
}

Suggestion for Claude Code Core

Claude Code should validate updatedInput against the tool's parameter schema before applying it. Unknown properties should be stripped or warned about. This would prevent hooks from accidentally corrupting any tool's input.

Fix Published

terrylica/cc-skills v11.63.1 — the pretooluse-subprocess-stdin-inlet-guard.ts hook now uses a PASSTHROUGH_TOOLS set.

Related: #29530, #13439, #10400

diakula · 6 months ago

having same when using GSD , from its discussion agent skill.

asem89 · 6 months ago

Confirmed reproduction — Claude Code v2.1.63, Linux VPS, GSD skills

Environment:

  • Claude Code v2.1.63
  • OS: Linux (Ubuntu) 6.8.0-100-generic
  • Permission mode: --dangerously-skip-permissions
  • No wildcard PreToolUse hooks (all hooks use specific matchers — Bash, not *)

Reproduction:
Calling AskUserQuestion from inside a GSD workflow skill (invoked via the Skill tool). The tool returns immediately with empty answers:

User has answered your questions: . You can now continue with the user's answers in mind.

The user never sees any prompt UI. The skill proceeds with no input.

Why the hook-corruption path (described in the last comment) doesn't apply here:
Our PreToolUse hooks use specific matchers (Bash only) and do not return updatedInput. Despite this, the bug still reproduces — which points to the original alwaysAllowRules early return described in the issue body, not the hook mutation path.

Context that may help:
The skill is invoked via the Skill tool (not a slash command directly). The GSD workflow skill calls AskUserQuestion mid-execution to gather user input (e.g. milestone scope selection). The Skill tool's contextModifier injects the skill's allowed-tools into alwaysAllowRules.command, which triggers the early return before requiresUserInteraction() is checked.

This is a regression — #9846 was closed as fixed in v2.0.28 but the fix only covered the bypassPermissions path, not the alwaysAllowRules path introduced by skill allowed-tools injection.

ekkerdthomas · 6 months ago

+1 — Hitting this exact bug on WSL2 (Linux 6.6.87.2-microsoft-standard-WSL2), Claude Code v2.1.63 with Opus 4.6.

Both our /brainstorm and /debug-app skills declare AskUserQuestion in allowed-tools. The tool auto-completes with empty answers — user never sees the prompt UI.

Workaround: Removing AskUserQuestion from allowed-tools in the skill frontmatter. The tool still works (Claude gets prompted for permission each time), and the UI renders correctly because it goes through checkPermissions()requiresUserInteraction() instead of the alwaysAllowRules early return.

The root cause analysis in this issue is spot-on. The suggested one-line fix (allowMatch && !tool.requiresUserInteraction?.()) would resolve this cleanly.

blois collaborator · 6 months ago

This will be fixed in an upcoming release.

mderzi · 5 months ago

still seeing it in Claude Code v2.1.68 on windows

PaulGlobee · 5 months ago

still seeing it in Claude Code v2.1.69 on macOs

github-actions[bot] · 5 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.