{"record":{"id":"4f880c3ed7a0d070","repo":"zeroclaw-labs/zeroclaw","slug":"acpchannel-does-not-support-reactions","errorCode":null,"errorMessage":"AcpChannel does not support reactions","messagePattern":"AcpChannel does not support reactions","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-channels/src/acp_channel.rs","lineNumber":327,"sourceCode":"            \"AcpChannel.listen is not supported (free-form ask_user awaits ACP elicitation Phase 2)\"\n        )\n    }\n\n    fn supports_free_form_ask(&self) -> bool {\n        false\n    }\n\n    async fn add_reaction(\n        &self,\n        _channel_id: &str,\n        _message_id: &str,\n        _emoji: &str,\n    ) -> anyhow::Result<()> {\n        // ACP renders agent output as message chunks — there's no per-message\n        // reaction primitive in the protocol, so silently no-oping (the trait\n        // default) would falsely report success to the agent. Surface as Err\n        // so the `reaction` tool's caller sees the truth.\n        anyhow::bail!(\"AcpChannel does not support reactions\")\n    }\n\n    async fn remove_reaction(\n        &self,\n        _channel_id: &str,\n        _message_id: &str,\n        _emoji: &str,\n    ) -> anyhow::Result<()> {\n        anyhow::bail!(\"AcpChannel does not support reactions\")\n    }\n\n    async fn request_choice(\n        &self,\n        question: &str,\n        choices: &[String],\n        timeout: Duration,\n    ) -> anyhow::Result<Option<String>> {\n        if choices.is_empty() {","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/acp_channel.rs#L309-L345","documentation":"ACP renders agent output as streamed message chunks and has no per-message reaction primitive, so add_reaction always returns an error. ZeroClaw refuses to silently no-op (the trait default would report success), so the reaction tool's caller learns the truth that the reaction cannot be delivered on this channel.","triggerScenarios":"The agent invokes the reaction tool (attach an emoji to a message) while the active channel is the ACP back-channel — i.e., during a session driven from an ACP editor/orchestrator client.","commonSituations":"Prompts and skills written for chat channels (Slack-style emoji acknowledgements) reused verbatim in ACP sessions; agent loops that add a reaction after every tool result.","solutions":["Gate the reaction tool by channel capability — skip or short-circuit it when the channel is ACP.","Replace the reaction with a text acknowledgement sent via Channel::send, which ACP supports as an agent_message_chunk.","Catch the error and tell the agent reactions are unsupported so it picks a different affordance."],"exampleFix":"// before\nch.add_reaction(channel_id, message_id, \"+1\").await?;\n\n// after\nif ch.name() == \"acp\" {\n    ch.send(&SendMessage::new(\"ack: done\", recipient)).await?;\n} else {\n    ch.add_reaction(channel_id, message_id, \"+1\").await?;\n}","handlingStrategy":"validation","validationCode":"fn supports_reactions(ch: &dyn Channel) -> bool {\n    // ACP has no per-message reaction primitive; add_reaction fails by design.\n    ch.name() != \"acp\"\n}","typeGuard":"fn is_reaction_capable(ch: &dyn Channel) -> bool {\n    ch.name() != \"acp\"\n}","tryCatchPattern":null,"preventionTips":["Advertise the reaction tool only for channels that implement reactions","Prefer text acknowledgements over emoji reactions in client-driven channels","Exercise prompt flows per channel kind in tests, not just once"],"tags":["rust","acp","reactions","unsupported-operation","channel-trait"],"backgroundTag":"unsupported-operation","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}