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
- 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
- On 401: force a token refresh (or restart the channel) — the cached access token is bad
- On 400/BAD_ID: verify the thing_id being replied to still exists and is fully formed (t1_/t3_ prefix)
- 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
- Pace comment replies well under Reddit's per-account limits — bursts guarantee 429/403 RATELIMIT
- Check the thing_id exists before replying (deleted comments give BAD_ID-style 400s)
- Distinguish 401 (token) from 403 (rate limit / blocked) before choosing a retry
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
- DM failed ({status}): {body}
- token refresh failed ({status}): {body}
- channel does not support room creation
- Discord send message failed ({status}): {err}
- send failed {context}: status={status}, body={body}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/457557a0c6241d1b.
Report an issue: GitHub.