{"record":{"id":"2b283b08ad3f09c7","repo":"zeroclaw-labs/zeroclaw","slug":"interaction-defer-failed-status-err","errorCode":null,"errorMessage":"interaction defer failed ({status}): {err}","messagePattern":"interaction defer failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/discord/interaction.rs","lineNumber":48,"sourceCode":"    interaction_token: &str,\n) -> anyhow::Result<()> {\n    let url = format!(\n        \"https://discord.com/api/v10/interactions/{interaction_id}/{interaction_token}/callback\"\n    );\n    // type 5 = DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE\n    let body = json!({ \"type\": 5 });\n    // without_url: reqwest transport errors embed the full request URL,\n    // which here contains the interaction token (a live credential).\n    let resp = client\n        .post(&url)\n        .json(&body)\n        .send()\n        .await\n        .map_err(reqwest::Error::without_url)?;\n    if !resp.status().is_success() {\n        let status = resp.status();\n        let err = resp.text().await.unwrap_or_default();\n        anyhow::bail!(\"interaction defer failed ({status}): {err}\");\n    }\n    Ok(())\n}\n\n/// Open a modal in response to a button/slash interaction (callback type 9).\n/// The caller registers the modal's `custom_id` in the pending registry as a\n/// resolve-into-turn so the eventual type-5 submit resolves it. Driven by the\n/// `OpenModal` dispatch arm (a `[COMPONENTS:…]` modal button click).\npub(crate) async fn discord_open_modal(\n    client: &reqwest::Client,\n    interaction_id: &str,\n    interaction_token: &str,\n    modal: &super::components::DiscordModal,\n) -> anyhow::Result<()> {\n    let Some(data) = modal.to_api() else {\n        anyhow::bail!(\"modal custom_id exceeds Discord's 100-char limit; cannot open\");\n    };\n    let url = format!(","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/discord/interaction.rs#L30-L66","documentation":"discord_defer_interaction POSTs a type-5 DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE callback to /interactions/{id}/{token}/callback to acknowledge within Discord's 3-second window. Non-2xx lands here with status and body. Classic causes: unknown or already-consumed interaction token (404/400) or a handler too slow to meet the window — the user then sees \"The application did not respond\". Transport errors are mapped through reqwest::Error::without_url so the token-bearing URL never leaks into logs.","triggerScenarios":"Slow work before the defer exceeds ~3s from interaction creation; deferring the same interaction twice (only one initial response is allowed); id/token mismatch or events replayed after a gateway resume; dispatch arm blocked on locks or cold caches.","commonSituations":"Agent performing tool calls or slow LLM inference before acknowledging; startup jank delaying the first callback; replaying captured interaction payloads in dev.","solutions":["Defer first, work second — move the defer to the very top of the interaction dispatch arm","If the body says Unknown interaction or already acknowledged, do not re-defer; answer via the followup webhook or drop the turn","Keep the pre-ack path free of awaits that can block (remote calls, lock contention)","Confirm gateway events arrive fresh (healthy connection, no long resume gaps)"],"exampleFix":"// before: heavy work then ack\nlet answer = run_agent(prompt).await?;\ndiscord_defer_interaction(&client, &id, &token).await?;\n\n// after: ack inside the 3s window, then work\ndiscord_defer_interaction(&client, &id, &token).await?;\nlet answer = run_agent(prompt).await?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = discord_defer_interaction(&client, &id, &token).await {\n    // token consumed/expired: do NOT re-defer — answer via the followup webhook or drop the turn\n    tracing::warn!(\"defer failed: {e}\");\n}","preventionTips":["Ack within 3 seconds: defer before any slow work","Send exactly one initial response per interaction","Treat defer failure as \"turn unanswerable\", not a retry candidate"],"tags":["discord","interactions","slash-commands","timing"],"backgroundTag":"discord-interaction-callback-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}