zeroclaw-labs/zeroclaw · error · anyhow::Error
elicitation returned unknown choice const: {const_value}
Error message
elicitation returned unknown choice const: {const_value} What it means
Phase 6 of `zeroclaw update` (src/commands/update.rs:292) — after the new binary is swapped in, smoke_test runs `<exe> --version`; a spawn failure or non-zero exit triggers rollback_binary to restore the Phase-3 `<exe>.bak` backup, then this bail. The system is left on the previous, working version. It means the downloaded binary passed download and validation (size, architecture, --version on the staged copy in Phase 4) but fails to start from its final installed location.
Source
Thrown at crates/zeroclaw-api/src/elicitation.rs:213
/// Decode the accepted `content` payload of an `elicitation/create`
/// single-select response back into the original display text.
///
/// Expects `content.choice` to be a `"choice-<idx>"` string whose
/// index is in bounds against `choices`. Returns the original text.
/// Returns `Err` if the field is missing, malformed, or out of range —
/// the same defense-in-depth posture the RFD recommends.
pub fn decode_single_select_accept(content: &Value, choices: &[String]) -> anyhow::Result<String> {
let const_value = content
.get("choice")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::Error::msg("elicitation accept missing content.choice string"))?;
let idx = const_value
.strip_prefix("choice-")
.and_then(|s| s.parse::<usize>().ok());
match idx.and_then(|i| choices.get(i)) {
Some(text) => Ok(text.clone()),
None => anyhow::bail!("elicitation returned unknown choice const: {const_value}"),
}
}
/// Decode the accepted `content` payload of an `elicitation/create`
/// multi-select response back into the original display texts.
pub fn decode_multi_select_accept(
content: &Value,
choices: &[String],
) -> anyhow::Result<Vec<String>> {
let arr = content
.get("choices")
.and_then(|v| v.as_array())
.ok_or_else(|| anyhow::Error::msg("elicitation accept missing content.choices array"))?;
let mut out = Vec::with_capacity(arr.len());
for v in arr {
let s = v
.as_str()
.ok_or_else(|| anyhow::Error::msg("non-string entry in content.choices"))?;View on GitHub (pinned to 88bb9c8533)
Solutions
- Confirm you are still on the old version (`zeroclaw --version`) — the rollback already restored it
- Reproduce the real startup error: download the release archive manually, run the extracted binary's --version directly, and check dynamic linking (ldd on Linux)
- If the prebuilt binary is incompatible with your system (glibc/runtime), install from source: ./install.sh --source (or --preset full / --features channels-full as needed)
- If the binary is genuinely broken on all hosts, report the release with the startup error and stay on your current version until a fixed release ships
Example fix
# before $ zeroclaw update Error: Update rolled back — smoke test failed: smoke test: updated binary returned non-zero exit code # after $ zeroclaw --version # still on the old (rolled-back) version $ # reproduce the real startup failure of the new binary: $ curl -fsSL <release-url>/zeroclaw-x86_64-unknown-linux-gnu.tar.gz -o /tmp/zc.tgz $ tar xzf /tmp/zc.tgz -C /tmp && ldd /tmp/zeroclaw | grep 'not found' $ ./install.sh --source # build locally if the prebuilt binary is incompatible
Defensive patterns
Strategy: fallback
Try / catch
if let Err(e) = update::run(version, force).await {
if e.to_string().contains("Update rolled back — smoke test failed") {
// Old version already restored. Verify with `zeroclaw --version`,
// keep the deployment pinned on the current version, and either
// install from source (./install.sh --source) or wait for a fixed
// release. Do not retry the same prebuilt artifact endlessly.
}
} Prevention
- On older distros or unusual platforms, prefer source installs over prebuilt release binaries
- Check release notes for runtime/glibc requirements before updating fleet hosts
- Watch the zeroclaw log's Phase 6 smoke-test entry to distinguish spawn failures from non-zero exits
When it happens
Trigger: `zeroclaw update` where the newly swapped binary exits non-zero or cannot be executed in place: host linker/runtime incompatibility (binary built against a newer glibc), missing shared library or VC/UCRT runtime, macOS Gatekeeper/quarantine blocking the replaced binary, or a broken release build that only fails at runtime.
Common situations: Prebuilt linux-gnu binary on an older distro (glibc too old); missing Visual C++ redistributable on Windows; Gatekeeper quarantine on macOS; a release whose binary was miscompiled or stripped of a needed dependency.
Related errors
- smoke test: updated binary returned non-zero exit code
- channel does not support room invites
- channel does not support room creation
- elicitation returned unknown choice const: {s}
- purge_namespace not supported by this memory backend
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/0cfe08d7cf9cc555.
Report an issue: GitHub.