xai-org/grok-build · error · ToolError

User question session ended unexpectedly (client may have di

Error message

User question session ended unexpectedly (client may have disconnected)

What it means

The ask_user_question tool sends a questionnaire to the client via a channel and blocks on a tokio oneshot receiver for the answers. This error is returned when the oneshot sender was dropped without ever sending a response (`RecvError`), meaning the session that was supposed to answer the question went away. The library throws it because the tool cannot produce a meaningful result once the answering side is gone.

Source

Thrown at crates/codegen/xai-grok-tools/src/implementations/grok_build/ask_user_question/mod.rs:465

            question_count,
            timeout_secs = ?wait.map(|d| d.as_secs()),
            "Asked user questions, blocking for response"
        );

        // ── Step 6: Block on the oneshot result (whole batch, one timer) ─
        // A single pending-decision timeout covers the questionnaire, not per
        // question: N questions in one call share one wait.
        // A `None` budget (`timeout_enabled = false`) runs the same await with
        // no timer, normalized into the timed shape so one match handles both.
        let outcome = match wait {
            Some(dur) => tokio::time::timeout(dur, result_rx).await,
            None => Ok(result_rx.await),
        };
        let result = match outcome {
            Ok(Ok(r)) => r,
            Ok(Err(_recv_error)) => {
                return Err(xai_tool_runtime::ToolError::execution(
                    xai_tool_protocol::ToolId::new("ask_user_question").expect("valid"),
                    "User question session ended unexpectedly (client may have disconnected)",
                ));
            }
            Err(_elapsed) => {
                tracing::info!(
                    question_count,
                    timeout_secs = ?wait.map(|d| d.as_secs()),
                    "User question timed out; continuing without answers"
                );
                // Drop the oneshot receiver on return. The shell coordinator
                // races `result_tx.closed()` against ACP so it unblocks and
                // can open the next questionnaire (stale UI is cancelled when
                // a new ext_method arrives). Same model text as cancel.
                return Ok(AskUserQuestionOutput::UserAnswered {
                    message: unanswered.to_string(),
                });
            }
        };

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Verify the client (ACP peer) is still connected and alive before invoking ask_user_question; reconnect the session and re-run the agent step.
  2. Check shell logs around the failure for coordinator/client disconnect or panic messages to identify why result_tx was dropped.
  3. Retry the tool call after restoring the session; the error is transient if the disconnect was temporary.
  4. If running non-interactively, configure non_interactive/wait budget params so the tool degrades to an unanswered result instead of blocking on a channel.

Example fix

// before: calling the tool from a script with no live client
await session.prompt("ask_user_question", { questions });
// after: guard on client connectivity / use non-interactive mode
if (!session.clientConnected()) {
  params.set("ask_user_question", { non_interactive: true });
}
await session.prompt("ask_user_question", { questions });
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling, ensure an interactive client is attached
if (!session.clientConnected || !session.clientConnected()) {
  params.set("ask_user_question", { non_interactive: true });
}

Type guard

fn has_live_client(res: &Resources) -> bool {
    res.get::<NotificationHandle>().is_some()
}

Try / catch

try {
  const out = await tool("ask_user_question", { questions });
} catch (e) {
  if (String(e.message).includes("session ended unexpectedly")) {
    await reconnect(); // or fall back to non-interactive defaults
  } else { throw e; }
}

Prevention

When it happens

Trigger: The coordinator/client holding `result_tx` is dropped before replying — e.g. the ACP client disconnects mid-questionnaire, the shell coordinator task is cancelled/panics, or the user closes the session while questions are pending. Distinguishable from a timeout: the wait budget did not elapse; the channel closed early.

Common situations: User closes the editor/CLI window while a question is pending; IDE extension host restarts during an agent run; the client process crashes or is killed; network drop tears down the ACP session; running the tool headlessly where no interactive client exists.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/5591fe968417ec05. Report an issue: GitHub.