zeroclaw-labs/zeroclaw · warning · anyhow::Error

Slack reactions.remove failed: {err}

Error message

Slack reactions.remove failed: {err}

What it means

Thrown when the Slack Web API `reactions.remove` call returns `ok: false` with an error code other than `no_reaction`. The library already treats `no_reaction` as success (the emoji is already gone), so any other Slack error code — message_not_found, too_old, cant_remove_reaction, not_authed — propagates with the raw code embedded in the message.

Source

Thrown at crates/zeroclaw-channels/src/slack.rs:5563

            .send()
            .await?;

        let status = resp.status();
        let text = resp.text().await.unwrap_or_default();

        if !status.is_success() {
            let sanitized = zeroclaw_providers::sanitize_api_error(&text);
            anyhow::bail!("Slack reactions.remove failed ({status}): {sanitized}");
        }

        let parsed: serde_json::Value = serde_json::from_str(&text).unwrap_or_default();
        if parsed.get("ok") == Some(&serde_json::Value::Bool(false)) {
            let err = parsed
                .get("error")
                .and_then(|e| e.as_str())
                .unwrap_or("unknown");
            if err != "no_reaction" {
                anyhow::bail!("Slack reactions.remove failed: {err}");
            }
        }

        Ok(())
    }

    async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {
        // Cache the bot user id on the struct so `self_handle` (sync,
        // called by the orchestrator's self-loop guard on every inbound)
        // resolves without an additional `auth.test` round-trip.
        self.cache_bot_user_id().await;
        let bot_user_id = self.get_bot_user_id().await.unwrap_or_default();
        let scoped_channels = self.scoped_channel_ids();
        if self.configured_app_token().is_some() {
            ::zeroclaw_log::record!(
                INFO,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),
                "channel listening in Socket Mode"

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the Slack error code embedded after 'failed:' — message_not_found/to_old mean the target is gone and can be treated as benign.
  2. Verify the bot token includes `reactions:write` and reinstall the Slack app if scopes changed.
  3. Confirm the `channel` + `timestamp` pair passed to reactions.remove comes from the same event payload that reported the reaction.
  4. On cleanup/dedup paths, catch the error and downgrade to a warning, mirroring the library's own no_reaction tolerance.

Example fix

// before
channel.remove_reaction(&channel_id, &ts, &emoji).await?;

// after
if let Err(e) = channel.remove_reaction(&channel_id, &ts, &emoji).await {
    let msg = e.to_string();
    if msg.contains("no_reaction") || msg.contains("message_not_found") || msg.contains("too_old") {
        tracing::warn!("reaction already gone: {e}");
    } else {
        return Err(e);
    }
}
Defensive patterns

Strategy: try-catch

Type guard

fn slack_reaction_error_code(err: &anyhow::Error) -> Option<&str> {
    err.to_string()
        .strip_prefix("Slack reactions.remove failed: ")
}

Try / catch

if let Err(e) = channel.remove_reaction(ch, ts, emoji).await {
    match slack_reaction_error_code(&e) {
        Some("no_reaction") | Some("message_not_found") | Some("too_old") => {
            tracing::warn!("reaction target gone: {e}");
        }
        _ => return Err(e),
    }
}

Prevention

When it happens

Trigger: Calling reactions.remove with a `timestamp` for a deleted message or one older than Slack's reaction window (message_not_found, too_old); a bot token missing the reactions:write scope (cant_remove_reaction, not_allowed); an invalid or revoked token (not_authed); a `channel` ID the bot cannot see (channel_not_found).

Common situations: Bot token regenerated and the app not reinstalled with the new scopes; retrying reaction cleanup after the target message was deleted; races where a parallel process removed the reaction; classic-bot vs workspace-app permission mismatches.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/c52718f4bb1d00ea. Report an issue: GitHub.