{"record":{"id":"e334d847873f6e52","repo":"zeroclaw-labs/zeroclaw","slug":"modal-open-failed-status-err","errorCode":null,"errorMessage":"modal open failed ({status}): {err}","messagePattern":"modal open failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/discord/interaction.rs","lineNumber":81,"sourceCode":"    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!(\n        \"https://discord.com/api/v10/interactions/{interaction_id}/{interaction_token}/callback\"\n    );\n    // type 9 = MODAL\n    let body = json!({ \"type\": 9, \"data\": data });\n    // without_url: transport errors embed the token-bearing URL.\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!(\"modal open failed ({status}): {err}\");\n    }\n    Ok(())\n}\n\npub(crate) async fn discord_answer_autocomplete(\n    client: &reqwest::Client,\n    interaction_id: &str,\n    interaction_token: &str,\n    choices: &[(String, String)],\n) -> anyhow::Result<()> {\n    let url = format!(\n        \"https://discord.com/api/v10/interactions/{interaction_id}/{interaction_token}/callback\"\n    );\n    let rendered: Vec<serde_json::Value> = choices\n        .iter()\n        .take(25)\n        .map(|(name, value)| json!({ \"name\": name, \"value\": value }))\n        .collect();","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/discord/interaction.rs#L63-L99","documentation":"After the local custom_id check passes, discord_open_modal POSTs the type-9 MODAL callback to /interactions/{id}/{token}/callback. Non-2xx becomes this error with the body. A modal is an initial response: it fails when the interaction was already acknowledged (e.g. after a defer), when the token is expired/unknown, or when the modal data violates Discord validation (rows/components structure; note the library pre-truncates to 5 rows and clamps labels/values, so those are unlikely causes here).","triggerScenarios":"Calling open_modal after defer_interaction on the same interaction (400 already acknowledged); token expired or unknown (404) from slow pre-work; malformed component JSON in the modal data; stale interactions after a gateway resume.","commonSituations":"Flow that defers \"for safety\" then decides to open a modal; policy checks running slow between receipt and open; component schema changes after a Discord API version bump.","solutions":["Pick exactly one initial response per interaction: modal XOR defer — never both","Open the modal immediately on the button/slash event, before await-heavy work","Read the body: 'interaction has already been acknowledged' vs 'Unknown interaction' point to different fixes","Re-verify modal shape against Discord limits (max 5 rows, one text input per row, label ≤45)"],"exampleFix":"// before: defer then try to open a modal\ndiscord_defer_interaction(&client, &id, &token).await?;\ndiscord_open_modal(&client, &id, &token, &modal).await?; // 400s\n\n// after: the modal is the sole initial response\ndiscord_open_modal(&client, &id, &token, &modal).await?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = discord_open_modal(&client, &id, &token, &modal).await {\n    // fall back to an ephemeral type-4 message so the user still gets feedback\n    let _ = discord_reject_interaction(&client, &id, &token, \"Could not open form\").await;\n}","preventionTips":["Never open a modal after deferring the same interaction","Open modals immediately on the triggering event","Log the body — 'already acknowledged' vs 'Unknown interaction' need different fixes"],"tags":["discord","modal","interactions","components"],"backgroundTag":"discord-interaction-callback-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}