zeroclaw-labs/zeroclaw · error · anyhow::Error
ACP returned unexpected outcome: {other}
Error message
ACP returned unexpected outcome: {other} What it means
Thrown by AcpChannel's legacy multiple-choice path (request_choice_via_permission) when the client's session/request_permission reply carries an outcome.outcome value other than "selected" or "cancelled". ZeroClaw maps each offered choice to a synthetic optionId (choice-0..n) and only understands those two outcome kinds; anything else — including a response that omits the nested outcome fields, which parses as an empty string — is treated as a client-side protocol violation. It usually indicates a buggy client, protocol revision drift, or a malformed JSON-RPC result.
Source
Thrown at crates/zeroclaw-channels/src/acp_channel.rs:121
.and_then(|o| o.get("outcome"))
.and_then(|s| s.as_str())
.unwrap_or("");
match kind {
"selected" => {
let option_id = outcome
.and_then(|o| o.get("optionId"))
.and_then(|s| s.as_str())
.unwrap_or("");
let idx = option_id
.strip_prefix("choice-")
.and_then(|s| s.parse::<usize>().ok());
match idx.and_then(|i| choices.get(i)) {
Some(text) => Ok(Some(text.clone())),
None => anyhow::bail!("ACP returned unknown optionId: {option_id}"),
}
}
"cancelled" => Ok(None),
other => anyhow::bail!("ACP returned unexpected outcome: {other}"),
}
}
/// Form-mode elicitation path — issues `elicitation/create` with a
/// single-select schema. Used when the client advertises
/// `clientCapabilities.elicitation.form`.
async fn request_choice_via_elicitation(
&self,
question: &str,
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),
};View on GitHub (pinned to 88bb9c8533)
Solutions
- Log the raw outcome object from the response to see exactly which string the client sent before the match arm fired.
- Update the ACP client to a protocol revision that replies with only selected or cancelled on session/request_permission.
- Have the client advertise clientCapabilities.elicitation.form so the modern elicitation/create path is used instead of the legacy overload.
- In the caller, degrade gracefully: log the error and treat it as a cancellation (None) rather than failing the whole agent turn.
Example fix
// before
let pick = channel.request_choice(question, &choices, timeout).await?;
// after
let pick = match channel.request_choice(question, &choices, timeout).await {
Ok(opt) => opt,
Err(e) if e.to_string().contains("unexpected outcome") => {
tracing::warn!(error = %e, "unrecognized ACP outcome; treating as cancel");
None
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Try / catch
match ch.request_choice(q, &choices, timeout).await {
Ok(Some(text)) => { /* proceed with the selection */ }
Ok(None) => { /* user cancelled */ }
Err(e) => {
tracing::warn!(error = %e, "choice prompt failed; treating as cancelled");
// treat as cancel: never guess a default option
}
} Prevention
- Prefer clients that advertise elicitation.form so the legacy permission overload is not used
- Pin ZeroClaw and the ACP client to the same protocol revision
- Log the raw outcome object when integrating a new ACP client
When it happens
Trigger: Calling Channel::request_choice on an AcpChannel whose client did not advertise clientCapabilities.elicitation.form (so the legacy session/request_permission overload is used), and the client replies with an unknown outcome kind, an empty outcome object, or a result shape missing outcome/outcome.
Common situations: Home-grown or early-version ACP clients (editors, orchestrators) sending values like "dismissed" or omitting the nested outcome object; protocol version drift after a client update; a client forwarding a permission response matched to the wrong request id.
Related errors
- ACP elicitation/create failed: {} ({})
- ACP returned unexpected permission outcome: {other}
- ACP elicitation/create timed out after {timeout:?}
- AcpChannel.request_choice requires at least one choice
- ACP elicitation/create (multi) failed: {} ({})
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/db3401a658c27b78.
Report an issue: GitHub.