xai-org/grok-build · error · ToolError
User question session ended unexpectedly (coordinator channe
Error message
User question session ended unexpectedly (coordinator channel closed)
What it means
The `ask_user_question` tool sends the question request to the coordinator over a channel; if `sender.0.send(request)` fails (receiver dropped), the coordinator session is gone, so the tool returns a ToolError with this message rather than panicking. Unlike the others this is a proper Err return, surfaced to the model/tool caller.
Source
Thrown at crates/codegen/xai-grok-tools/src/implementations/grok_build/ask_user_question/mod.rs:421
"missing_resource",
"UserQuestionSender".to_string(),
));
}
};
// ── Step 3: Create oneshot ──────────────────────────────────────
let (result_tx, result_rx) = tokio::sync::oneshot::channel();
// ── Step 4: Send UserQuestionRequest ────────────────────────────
let request = types::UserQuestionRequest {
tool_call_id: ctx.call_id.as_str().to_owned(),
questions: input.questions.clone(),
result_tx,
};
if sender.0.send(request).is_err() {
return Err(xai_tool_runtime::ToolError::execution(
xai_tool_protocol::ToolId::new("ask_user_question").expect("valid"),
"User question session ended unexpectedly (coordinator channel closed)",
));
}
// ── Step 5: Emit UserQuestionAsked + read the wait budget ───────
let params = {
let questions_json = serde_json::to_value(&input.questions)
.unwrap_or_else(|_| serde_json::Value::Array(vec![]));
let res = resources.lock().await;
if let Some(handle) = res.get::<NotificationHandle>() {
handle.0.send_user_question_asked(UserQuestionAsked {
tool_call_id: ctx.call_id.as_str().to_owned(),
questions_json,
});
}
// Shell-injected params win; absent or unset fields keep the legacy
// env→default budget so non-shell registry consumers are unchanged.
res.get::<crate::types::resources::Params<AskUserQuestionParams>>()View on GitHub (pinned to bc7f02eddd)
Solutions
- Only invoke ask_user_question within an active interactive session with a live coordinator.
- Check coordinator lifecycle logs for an earlier panic/exit that dropped the receiver.
- Add a readiness check or keep-alive on the coordinator channel before dispatching questions.
- Handle the ToolError upstream by skipping the question and continuing with defaults instead of failing the whole turn.
Example fix
// before
if sender.0.send(request).is_err() {
return Err(..."User question session ended unexpectedly (coordinator channel closed)"...);
}
// after (caller side)
match tool.run(input).await {
Err(e) if e.to_string().contains("coordinator channel closed") => proceed_with_defaults(input),
other => other,
} Defensive patterns
Strategy: fallback
Validate before calling
// check the coordinator channel is still open before asking
if sender.0.is_closed() {
return proceed_with_defaults(input.questions);
} Try / catch
match sender.0.send(request) {
Ok(()) => { /* await response */ }
Err(_) => {
tracing::warn!("coordinator gone; skipping user question");
proceed_with_defaults(input.questions)
}
} Prevention
- Invoke ask_user_question only inside live interactive sessions
- Monitor coordinator task health; restart it before accepting tool calls
- Design tool callers to degrade to defaults when the question channel is closed
When it happens
Trigger: The coordinator handling user questions is closed or dropped before/while the tool sends its request — e.g. the interactive session ended, the UI side shut down, or the tool ran outside a live user-question session.
Common situations: Running the ask_user_question tool in a non-interactive/headless context where no coordinator exists; session timeout already torn down the coordinator; a prior error crashed the coordinator task; tool invoked after the user closed the prompt UI.
Related errors
- PTY write channel closed
- failed to build request: {e}
- response parse error: {e}
- ACP response missing result field
- bridge spawn
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/a5050830e1c47473.
Report an issue: GitHub.