{"record":{"id":"457557a0c6241d1b","repo":"zeroclaw-labs/zeroclaw","slug":"comment-reply-failed-status-body","errorCode":null,"errorMessage":"comment reply failed ({status}): {body}","messagePattern":"comment reply failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/reddit.rs","lineNumber":301,"sourceCode":"            // Comment reply\n            let resp = client\n                .post(format!(\"{REDDIT_API_BASE}/api/comment\"))\n                .bearer_auth(&token)\n                .header(\"User-Agent\", USER_AGENT)\n                .form(&[\n                    (\"thing_id\", message.recipient.as_str()),\n                    (\"text\", &message.content),\n                ])\n                .send()\n                .await?;\n\n            let status = resp.status();\n            if !status.is_success() {\n                let body = resp\n                    .text()\n                    .await\n                    .unwrap_or_else(|e| format!(\"<failed to read response: {e}>\"));\n                bail!(\"comment reply failed ({status}): {body}\");\n            }\n        } else {\n            // Direct message\n            let subject = message\n                .subject\n                .as_deref()\n                .unwrap_or(\"Message from ZeroClaw\");\n            let resp = client\n                .post(format!(\"{REDDIT_API_BASE}/api/compose\"))\n                .bearer_auth(&token)\n                .header(\"User-Agent\", USER_AGENT)\n                .form(&[\n                    (\"to\", message.recipient.as_str()),\n                    (\"subject\", subject),\n                    (\"text\", &message.content),\n                ])\n                .send()\n                .await?;","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/reddit.rs#L283-L319","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(err) = channel.send(&reply_target, &text).await {\n    let msg = format!(\"{err:#}\");\n    if msg.starts_with(\"comment reply failed (429\") || msg.contains(\"RATELIMIT\") {\n        tokio::time::sleep(Duration::from_secs(60 * 10)).await; // Reddit rate limits need long backoff\n        return channel.send(&reply_target, &text).await;\n    }\n    if msg.starts_with(\"comment reply failed (401\") {\n        channel.force_token_refresh().await;\n        return channel.send(&reply_target, &text).await;\n    }\n    return Err(err);\n}","preventionTips":["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"],"tags":["reddit","comment-reply","rate-limit","http-status","api","channel"],"backgroundTag":"reddit-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}