zeroclaw-labs/zeroclaw · warning · anyhow::Error
AcpChannel does not support reactions
Error message
AcpChannel does not support reactions
What it means
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.
Source
Thrown at crates/zeroclaw-channels/src/acp_channel.rs:327
"AcpChannel.listen is not supported (free-form ask_user awaits ACP elicitation Phase 2)"
)
}
fn supports_free_form_ask(&self) -> bool {
false
}
async fn add_reaction(
&self,
_channel_id: &str,
_message_id: &str,
_emoji: &str,
) -> anyhow::Result<()> {
// ACP renders agent output as message chunks — there's no per-message
// reaction primitive in the protocol, so silently no-oping (the trait
// default) would falsely report success to the agent. Surface as Err
// so the `reaction` tool's caller sees the truth.
anyhow::bail!("AcpChannel does not support reactions")
}
async fn remove_reaction(
&self,
_channel_id: &str,
_message_id: &str,
_emoji: &str,
) -> anyhow::Result<()> {
anyhow::bail!("AcpChannel does not support reactions")
}
async fn request_choice(
&self,
question: &str,
choices: &[String],
timeout: Duration,
) -> anyhow::Result<Option<String>> {
if choices.is_empty() {View on GitHub (pinned to 88bb9c8533)
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.
Example fix
// before
ch.add_reaction(channel_id, message_id, "+1").await?;
// after
if ch.name() == "acp" {
ch.send(&SendMessage::new("ack: done", recipient)).await?;
} else {
ch.add_reaction(channel_id, message_id, "+1").await?;
} Defensive patterns
Strategy: validation
Validate before calling
fn supports_reactions(ch: &dyn Channel) -> bool {
// ACP has no per-message reaction primitive; add_reaction fails by design.
ch.name() != "acp"
} Type guard
fn is_reaction_capable(ch: &dyn Channel) -> bool {
ch.name() != "acp"
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- AcpChannel.listen is not supported (free-form ask_user await
- ACP returned unexpected outcome: {other}
- ACP elicitation/create failed: {} ({})
- ACP elicitation/create timed out after {timeout:?}
- AcpChannel.request_choice requires at least one choice
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/4f880c3ed7a0d070.
Report an issue: GitHub.