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

Discord add reaction failed ({status}): {err}

Error message

Discord add reaction failed ({status}): {err}

What it means

add_reaction PUTs /channels/{channel_id}/messages/{message_id}/reactions/{emoji}/@me (URL-encoded by discord_reaction_url, skin-tone variants reduced to the base glyph). Non-2xx becomes this error with the response body. Interaction sentinel targets are filtered before the call since they are not real channels. Dominant causes: 403 50001 Missing Permissions (ADD_REACTIONS), 10014 Unknown Emoji (custom emoji from another guild or name typo), 429 rate limiting, 404 deleted message/channel.

Source

Thrown at crates/zeroclaw-channels/src/discord/mod.rs:3801

            return Ok(());
        }
        let url = discord_reaction_url(channel_id, message_id, emoji);

        let resp = self
            .http_client()
            .put(&url)
            .header("Authorization", format!("Bot {}", self.bot_token))
            .header("Content-Length", "0")
            .send()
            .await?;

        if !resp.status().is_success() {
            let status = resp.status();
            let err = resp
                .text()
                .await
                .unwrap_or_else(|e| format!("<failed to read response body: {e}>"));
            anyhow::bail!("Discord add reaction failed ({status}): {err}");
        }

        Ok(())
    }

    async fn remove_reaction(
        &self,
        channel_id: &str,
        message_id: &str,
        emoji: &str,
    ) -> anyhow::Result<()> {
        // Interaction sentinels are not channel ids (see add_reaction).
        if parse_discord_interaction_target(channel_id).is_some() {
            return Ok(());
        }
        let url = discord_reaction_url(channel_id, message_id, emoji);

        let resp = self

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Match the body: 50001 → grant Add Reactions; 10014 → use a guild-available or standard unicode emoji; 429 → back off; 10008 → message gone, skip
  2. Prefer standard unicode glyphs for signal reactions instead of custom server emoji
  3. Grant the bot ADD_REACTIONS wherever it posts
  4. Treat reaction failure as best-effort: log it, never fail the send
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = channel.add_reaction(ch, msg_id, "⚠️").await {
    tracing::warn!("signal reaction failed: {e}"); // decoration only; never fail the send
}

Prevention

When it happens

Trigger: Bot lacks Add Reactions on the target channel; using a :name:id: custom emoji not present in that guild; unicode emoji mangled by encoding (variation selectors stripped); reacting to a message deleted between send and react; reaction bursts hitting per-channel limits.

Common situations: Failure-signal reactions (⚠️ from decide_failure_reactions) applied in channels where the bot only has Send Messages; emoji copied from another server; restrictive role overrides.

Related errors


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