{"record":{"id":"461861d02edd2690","repo":"zeroclaw-labs/zeroclaw","slug":"delete-message-failed-status-err","errorCode":null,"errorMessage":"delete message failed ({status}): {err}","messagePattern":"delete message failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-channels/src/discord/rest.rs","lineNumber":256,"sourceCode":"        .send()\n        .await?;\n\n    if resp.status().as_u16() == 429 {\n        ::zeroclaw_log::record!(\n            DEBUG,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),\n            \"delete message rate-limited (429), skipping\"\n        );\n        return Ok(());\n    }\n\n    if !resp.status().is_success() {\n        let status = resp.status();\n        let err = resp\n            .text()\n            .await\n            .unwrap_or_else(|e| format!(\"<failed to read response body: {e}>\"));\n        anyhow::bail!(\"delete message failed ({status}): {err}\");\n    }\n\n    Ok(())\n}\n\npub(crate) fn encode_emoji_for_discord(emoji: &str) -> String {\n    if emoji.contains(':') {\n        return emoji.to_string();\n    }\n\n    let mut encoded = String::new();\n    for byte in emoji.as_bytes() {\n        let _ = write!(encoded, \"%{byte:02X}\");\n    }\n    encoded\n}\n\npub(crate) fn discord_reaction_url(channel_id: &str, message_id: &str, emoji: &str) -> String {","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/discord/rest.rs#L238-L274","documentation":"delete_discord_message DELETEs a message and bails on non-success status. It is used by draft finalize/cancel to remove the provisional message once the flow completes. Deletion is naturally idempotent, so 404 (already gone) is the most common and most benign trigger.","triggerScenarios":"DELETE /channels/{id}/messages/{id} returns 404 (message already deleted by a user or another flow), 403 (missing Manage Messages when deleting others' messages), 429.","commonSituations":"User manually deletes the draft message before the agent finishes; bot lacks Manage Messages; rate-limited cleanup during bursts.","solutions":["404 → nothing to do; the desired end state already holds — treat as success","403 → grant Manage Messages on the channel (only needed for messages the bot did not send)","429 → retry with Retry-After backoff"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match delete_discord_message(&client, &url).await {\n    Ok(()) => Ok(()),\n    Err(e) if e.to_string().contains(\"404\") => Ok(()), // already gone: idempotent success\n    Err(e) => Err(e),\n}","preventionTips":["Deletion is idempotent — always treat 404 as success","Grant Manage Messages only if the bot must delete messages it did not send"],"tags":["discord","delete-message","http-status","draft-cleanup","idempotent"],"backgroundTag":"discord-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}