{"record":{"id":"3bce9572f3781856","repo":"zeroclaw-labs/zeroclaw","slug":"interaction-autocomplete-answer-failed-status","errorCode":null,"errorMessage":"interaction autocomplete answer failed ({status}): {err}","messagePattern":"interaction autocomplete answer failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-channels/src/discord/interaction.rs","lineNumber":112,"sourceCode":"    );\n    let rendered: Vec<serde_json::Value> = choices\n        .iter()\n        .take(25)\n        .map(|(name, value)| json!({ \"name\": name, \"value\": value }))\n        .collect();\n    // type 8 = APPLICATION_COMMAND_AUTOCOMPLETE_RESULT\n    let body = json!({ \"type\": 8, \"data\": { \"choices\": rendered } });\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!(\"interaction autocomplete answer failed ({status}): {err}\");\n    }\n    Ok(())\n}\n\n/// Extract a string option (`d.data.options[name].value`) from an\n/// APPLICATION_COMMAND interaction payload. Empty string when absent.\npub(crate) fn interaction_string_option(d: &serde_json::Value, name: &str) -> String {\n    d.get(\"data\")\n        .and_then(|x| x.get(\"options\"))\n        .and_then(|o| o.as_array())\n        .and_then(|opts| {\n            opts.iter()\n                .find(|o| o.get(\"name\").and_then(|n| n.as_str()) == Some(name))\n        })\n        .and_then(|o| o.get(\"value\"))\n        .and_then(|v| v.as_str())\n        .unwrap_or(\"\")\n        .to_string()","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/discord/interaction.rs#L94-L130","documentation":"discord_answer_autocomplete answers an APPLICATION_COMMAND_AUTOCOMPLETE interaction with a type-8 callback carrying up to 25 choices (the code pre-clamps with .take(25), so count is never the failure). Non-2xx becomes this error. Autocomplete tokens are the shortest-lived interaction credentials — Discord expects the answer within roughly 3 seconds of the keystroke — so slow generation and stale requests dominate; sending type-8 to a non-autocomplete interaction also 400s.","triggerScenarios":"Option generation (LLM call, DB query) finishing after the interaction expired; answering after the user already submitted or aborted (token consumed); choice names/values exceeding Discord length limits from user data; interaction type mismatch.","commonSituations":"Autocomplete backed by an LLM taking seconds per keystroke; cold caches on first input; dev replay of old interaction payloads.","solutions":["Make generation fast: local prefix index, cache, or precomputed choice list — target well under 1 second","Drop stale requests: when a newer keystroke arrived, skip answering the older interaction","Validate choice name/value lengths before sending (string name/value ≤100)","Treat failure as UX degradation — log and move on; the user simply sees no suggestions"],"exampleFix":"// before: fresh LLM call per keystroke, seconds late\nlet choices = llm_suggest(&q).await;\ndiscord_answer_autocomplete(&client, &id, &token, &choices).await?;\n\n// after: prefix search over a local index\nlet choices = index.search(&q, 25);\ndiscord_answer_autocomplete(&client, &id, &token, &choices).await?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = discord_answer_autocomplete(&client, &id, &token, &choices).await {\n    tracing::debug!(\"autocomplete missed: {e}\"); // best-effort UX, never fatal\n}","preventionTips":["Answer autocomplete from a local index/cache in well under a second","Drop stale autocomplete requests when a newer keystroke arrived","Keep choice names/values within Discord's 100-char limit"],"tags":["discord","autocomplete","interactions","performance"],"backgroundTag":"discord-interaction-callback-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}