zeroclaw-labs/zeroclaw · error
RPC elicitation/create failed: {} ({})
Error message
RPC elicitation/create failed: {} ({}) What it means
request_choice_via_elicitation implements approval prompts over RPC: it serializes an ElicitationRequest (form mode, single-select schema) and calls rpc.request("elicitation/create", ...). If the RPC call itself completes with an error — as opposed to timing out, which is error 879 — it bails with the transport error's message and code. Root causes live on the RPC/client side: connection dropped mid-call, method unsupported by the connected client, or protocol-level rejection.
Source
Thrown at crates/zeroclaw-runtime/src/rpc/approval_channel.rs:237
choices: &[String],
timeout: Duration,
) -> anyhow::Result<Option<String>> {
let req = ElicitationRequest {
session_id: self.session_id.clone(),
mode: ElicitationMode::Form,
message: question.to_string(),
requested_schema: single_select_schema(choices),
};
debug_assert!(
matches!(req.mode, ElicitationMode::Form),
"Phase 1 must not emit URL-mode elicitation"
);
let params = serde_json::to_value(&req)?;
let call = self.rpc.request("elicitation/create", params);
let response_value = match tokio::time::timeout(timeout, call).await {
Ok(Ok(value)) => value,
Ok(Err(e)) => {
anyhow::bail!("RPC elicitation/create failed: {} ({})", e.message, e.code)
}
Err(_) => anyhow::bail!("RPC elicitation/create timed out after {timeout:?}"),
};
let parsed: ElicitationResponse = serde_json::from_value(response_value)
.map_err(|e| anyhow::Error::msg(format!("malformed elicitation response: {e}")))?;
match parsed {
ElicitationResponse::Accept { content } => {
let text = decode_single_select_accept(&content, choices)?;
Ok(Some(text))
}
ElicitationResponse::Decline | ElicitationResponse::Cancel => Ok(None),
}
}
/// Form-mode elicitation multi-select path — same wire shape as
/// `AcpChannel::request_multi_choice`.
async fn request_multi_choice_via_elicitation(
&self,View on GitHub (pinned to 88bb9c8533)
Solutions
- Classify by the embedded (code): connection-closed codes mean the client went away — retry only after reconnect; unknown-method codes mean the client lacks elicitation support — upgrade the client/daemon pair
- Upgrade both sides so the client speaks the same elicitation/create RPC version
- Verify the session id passed to the approval channel is the live session
- Handle the failure as an unanswered prompt: fall back to deny/default (the surrounding channel code already maps unreachable clients to Deny for other paths)
Example fix
// before
let choice = channel.request_choice(&q, &choices, timeout).await?;
// after
match channel.request_choice(&q, &choices, timeout).await {
Ok(Some(c)) => c,
Ok(None) => default_choice(), // declined/cancelled
Err(e) if e.to_string().contains("elicitation/create failed") => {
tracing::warn!("client unreachable: {e}");
default_choice() // treat as deny/default
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
// before prompting, confirm the RPC peer is alive and speaks elicitation
if !rpc.connected() { return Ok(default_choice()); }
// optionally negotiate capabilities at session start and cache `supports_elicitation` Try / catch
match channel.request_choice(&q, &choices, timeout).await {
Ok(Some(c)) => c,
Ok(None) => default_choice(),
Err(e) if e.to_string().contains("elicitation/create failed") => {
// client-side RPC failure: classify by the embedded code; treat as unanswered → deny/default
default_choice()
}
Err(e) => Err(e),
} Prevention
- Upgrade daemon and client together to keep elicitation/create versions aligned
- Handle client-gone as a normal deny path, matching the channel's Unreachable→Deny semantics
- Negotiate client capabilities at connect time and skip elicitation when unsupported
When it happens
Trigger: The connected client (IDE/editor/host) disconnects while the approval prompt is pending; the client predates the elicitation/create method so the peer returns a method-not-found style error; serialization/session problems such as an invalid session_id in the request; the RPC channel was already closed during shutdown, so the request errors instead of hanging.
Common situations: Operator closes their client right when a sensitive action asks for approval; version skew between daemon and client after a partial upgrade; sessions expiring server-side between the call and the response; test harnesses with stub RPC peers that error on unknown methods.
Related errors
- RPC elicitation/create timed out after {timeout:?}
- ACP elicitation/create failed: {} ({})
- ACP elicitation/create timed out after {timeout:?}
- ACP elicitation/create (multi) failed: {} ({})
- ACP elicitation/create (multi) timed out after {timeout:?}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/1de4698ab8813bbe.
Report an issue: GitHub.