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

comment reply failed ({status}): {body}

Error message

comment reply failed ({status}): {body}

What it means

Raised by RedditChannel::send when POST {REDDIT_API_BASE}/api/comment (replying to a comment) returns a non-2xx status; the body text is included. Reddit signals most application-level failures this way: 403 with 'You are doing that too much' for RATELIMIT, 400 for malformed/absent thing ids, 401 for an expired/invalid bearer token. It only fires on the comment-reply branch of send (the DM branch produces 'DM failed').

Source

Thrown at crates/zeroclaw-channels/src/reddit.rs:301

            // Comment reply
            let resp = client
                .post(format!("{REDDIT_API_BASE}/api/comment"))
                .bearer_auth(&token)
                .header("User-Agent", USER_AGENT)
                .form(&[
                    ("thing_id", message.recipient.as_str()),
                    ("text", &message.content),
                ])
                .send()
                .await?;

            let status = resp.status();
            if !status.is_success() {
                let body = resp
                    .text()
                    .await
                    .unwrap_or_else(|e| format!("<failed to read response: {e}>"));
                bail!("comment reply failed ({status}): {body}");
            }
        } else {
            // Direct message
            let subject = message
                .subject
                .as_deref()
                .unwrap_or("Message from ZeroClaw");
            let resp = client
                .post(format!("{REDDIT_API_BASE}/api/compose"))
                .bearer_auth(&token)
                .header("User-Agent", USER_AGENT)
                .form(&[
                    ("to", message.recipient.as_str()),
                    ("subject", subject),
                    ("text", &message.content),
                ])
                .send()
                .await?;

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. On 429 or a body containing RATELIMIT: stop and back off substantially (minutes), and reduce reply frequency — Reddit's limits are per-account and strict
  2. On 401: force a token refresh (or restart the channel) — the cached access token is bad
  3. On 400/BAD_ID: verify the thing_id being replied to still exists and is fully formed (t1_/t3_ prefix)
  4. Check the account status in a browser (shadowban/lock) if every reply fails with 403 but rate limits are not the cause
Defensive patterns

Strategy: retry

Try / catch

if let Err(err) = channel.send(&reply_target, &text).await {
    let msg = format!("{err:#}");
    if msg.starts_with("comment reply failed (429") || msg.contains("RATELIMIT") {
        tokio::time::sleep(Duration::from_secs(60 * 10)).await; // Reddit rate limits need long backoff
        return channel.send(&reply_target, &text).await;
    }
    if msg.starts_with("comment reply failed (401") {
        channel.force_token_refresh().await;
        return channel.send(&reply_target, &text).await;
    }
    return Err(err);
}

Prevention

When it happens

Trigger: send() is called with a reply_target that resolves to a comment (t1_...) or submission, building POST /api/comment with text + thing_id; a non-success status — 403 RATELIMIT, 400 BAD_ID, 401 token expired, 403 blocked/banned — triggers the bail.

Common situations: Bots replying to every mention quickly hitting Reddit's per-account rate limits; shadowbanned or restricted bot accounts; replying to a deleted comment (thing id no longer valid); access token expired mid-run so the bearer auth fails.

Related errors


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