{"record":{"id":"b41f668492523b55","repo":"zeroclaw-labs/zeroclaw","slug":"acp-returned-unexpected-permission-outcome-other","errorCode":null,"errorMessage":"ACP returned unexpected permission outcome: {other}","messagePattern":"ACP returned unexpected permission outcome: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/acp_channel.rs","lineNumber":542,"sourceCode":"                            .unwrap_or(\"\")\n                            .to_string();\n                        ChannelApprovalResponse::DenyWithEdit { replacement }\n                    }\n                    other => anyhow::bail!(\"ACP returned unknown permission optionId: {other}\"),\n                };\n                Ok(Some(\n                    zeroclaw_api::channel::AttributedApprovalResponse::operator(response),\n                ))\n            }\n            // The client withdrew the prompt; nobody chose anything. Still a\n            // deny, but the runtime's, not the operator's.\n            \"cancelled\" => Ok(Some(\n                zeroclaw_api::channel::AttributedApprovalResponse::from_runtime(\n                    ChannelApprovalResponse::Deny,\n                    zeroclaw_api::channel::ApprovalSource::Unreachable,\n                ),\n            )),\n            other => anyhow::bail!(\"ACP returned unexpected permission outcome: {other}\"),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use tokio::sync::mpsc;\n    use zeroclaw_api::elicitation::single_select_schema_with_property_name;\n    use zeroclaw_api::jsonrpc::JSONRPC_VERSION;\n\n    fn make_rpc() -> (Arc<RpcOutbound>, mpsc::Receiver<String>) {\n        let (tx, rx) = mpsc::channel::<String>(16);\n        (Arc::new(RpcOutbound::new(tx)), rx)\n    }\n\n    #[tokio::test]\n    async fn name_returns_provided_name() {","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/acp_channel.rs#L524-L560","documentation":"The approval response's outcome.outcome was neither \"selected\" nor \"cancelled\" — the only two outcomes request_approval_attributed understands. As with the choice path, a response missing the nested outcome field parses as an empty string and lands here too. It signals a malformed or version-drifted client response, not an operator decision.","triggerScenarios":"session/request_permission for an approval returns an outcome object with an unknown kind string or without the nested outcome/outcome field — e.g. a client sending a flat or differently-named shape.","commonSituations":"Protocol revision drift after a client update; incomplete client implementations; response shapes copied from a different JSON-RPC method.","solutions":["Capture and log the raw outcome object to see the exact value the client sent.","Align the client with the ACP permission response schema: {outcome: {outcome: selected|cancelled, ...}}.","Map this error to a runtime Deny in the caller so the gated tool does not run.","Add a client-side regression test that replies with only the two legal outcome kinds."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Any unparseable approval outcome is a runtime Deny (like 'cancelled'),\n// attributed to the runtime rather than the operator.\nlet resp = ch.request_approval(recipient, &req).await\n    .unwrap_or(Some(ChannelApprovalResponse::Deny))\n    .unwrap_or(ChannelApprovalResponse::Deny);","preventionTips":["Treat any unparseable approval outcome as Deny, never Approve","Pin protocol revisions between ZeroClaw and the client","Validate client response shapes in integration tests before shipping"],"tags":["rust","acp","approval","protocol-violation","jsonrpc"],"backgroundTag":"invalid-protocol-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}