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

ACP elicitation/create (multi) timed out after {timeout:?}

Error message

ACP elicitation/create (multi) timed out after {timeout:?}

What it means

The multi-select elicitation/create request got no answer before the caller-supplied timeout elapsed (tokio::time::timeout). Like the single-select path, this prevents an unresponsive client from parking the poll tool forever; the elapsed Duration is included in the message.

Source

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

        }

        let req = ElicitationRequest {
            session_id: self.session_id.clone(),
            mode: ElicitationMode::Form,
            message: question.to_string(),
            requested_schema: multi_select_schema(choices, min_items, max_items),
        };
        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 (multi) failed: {} ({})",
                e.message,
                e.code
            ),
            Err(_) => {
                anyhow::bail!("ACP elicitation/create (multi) 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 texts =
                    zeroclaw_api::elicitation::decode_multi_select_accept(&content, choices)?;
                Ok(Some(texts))
            }
            ElicitationResponse::Decline | ElicitationResponse::Cancel => Ok(None),
        }
    }

    /// Delegates to [`Self::request_approval_attributed`] and drops the
    /// provenance, so the prompt logic lives in exactly one place.
    async fn request_approval(

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Raise the timeout passed to request_multi_choice to fit human form-filling time.
  2. Check that the ACP client and transport are alive before issuing the prompt.
  3. Treat the timeout as a cancellation (None) in the caller instead of an error, if proceeding without the selection is safe.
  4. Cancel in-flight prompts during session teardown so timeouts do not race shutdown.
Defensive patterns

Strategy: retry

Try / catch

match ch.request_multi_choice(q, &choices, min, max, timeout).await {
    Ok(opt) => opt,
    Err(e) if e.to_string().contains("timed out") => {
        tracing::warn!(error = %e, "multi-select timed out; treating as declined");
        None
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: request_multi_choice on a form-capable client where no JSON-RPC response arrives in time — the user never submits the multi-select form, the client exits, or the transport stalls.

Common situations: Multi-select forms left open because users must check several items; unattended sessions; network drops mid-prompt; timeouts copied from single-select defaults that are too short.

Understand the failure class

Related errors


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