zeroclaw-labs/zeroclaw · error · anyhow::Error

ACP elicitation/create failed: {} ({})

Error message

ACP elicitation/create failed: {} ({})

What it means

The JSON-RPC request elicitation/create itself returned an error response (message + code) instead of a result. This is the form-mode path used when the client advertises clientCapabilities.elicitation.form; the code and message embedded in the error come straight from the client, so they state why the elicitation was refused (method not implemented, invalid params, unknown sessionId).

Source

Thrown at crates/zeroclaw-channels/src/acp_channel.rs:150

        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!("ACP elicitation/create failed: {} ({})", e.message, e.code)
            }
            Err(_) => anyhow::bail!("ACP 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 =
                    zeroclaw_api::elicitation::decode_single_select_accept(&content, choices)?;
                Ok(Some(text))
            }
            ElicitationResponse::Decline | ElicitationResponse::Cancel => Ok(None),
        }
    }
}

impl ::zeroclaw_api::attribution::Attributable for AcpChannel {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the (code, message) pair embedded in the error text — it is the client's own stated reason for the refusal.
  2. Verify the session still exists on the client; re-run session/new and retry with a fresh sessionId if the client restarted.
  3. Confirm the client genuinely implements elicitation/create; if not, stop advertising elicitation.form so the legacy permission path is used.
  4. Align ZeroClaw and the client on protocol revisions that agree on the elicitation schema.
Defensive patterns

Strategy: try-catch

Type guard

fn client_supports_form_elicitation(caps: &ElicitationCapabilities) -> bool {
    caps.form
}

Try / catch

if let Err(e) = ch.request_choice(q, &choices, timeout).await {
    if e.to_string().contains("elicitation/create failed") {
        // client refused the prompt: re-create the session or surface
        // 'could not ask the user' to the agent instead of retrying blindly
    }
}

Prevention

When it happens

Trigger: AcpChannel::request_choice on a form-capable client where elicitation/create is rejected: the client advertises elicitation.form but does not implement the handler, the sessionId is stale after a client restart, or the single-select requestedSchema fails the client's validation.

Common situations: Capability handshake lying (form advertised, handler missing); session id from a previous connection reused after reconnect; schema-shape drift between the ZeroClaw version emitting requestedSchema and the client version validating it.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/8277a4c8fbd5cdb4. Report an issue: GitHub.