{"record":{"id":"ee86132ad9d16569","repo":"zeroclaw-labs/zeroclaw","slug":"listing-commands-failed","errorCode":null,"errorMessage":"listing commands failed ({})","messagePattern":"listing commands failed \\((.+?)\\)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/discord/slash.rs","lineNumber":636,"sourceCode":"    // so the fingerprint is not recorded and the next READY retries.\n    let mut failed_deletes = 0usize;\n    // `with_localizations=true` so the listing echoes back the full\n    // `*_localizations` dictionaries; without it Discord returns them null and\n    // every localized command would mismatch the projection and re-register on\n    // each READY (burning the daily command-create budget).\n    let resp = client\n        .get(format!(\"{base}?with_localizations=true\"))\n        .header(\"Authorization\", auth)\n        .send()\n        .await\n        .map_err(reqwest::Error::without_url)?;\n    if resp.status() == reqwest::StatusCode::TOO_MANY_REQUESTS {\n        return Ok(ReconcileOutcome::RateLimited {\n            until: rate_limit_deadline(resp).await,\n        });\n    }\n    if !resp.status().is_success() {\n        anyhow::bail!(\"listing commands failed ({})\", resp.status());\n    }\n    let existing: Vec<serde_json::Value> =\n        resp.json().await.map_err(reqwest::Error::without_url)?;\n    for cmd in &existing {\n        let name = cmd.get(\"name\").and_then(|n| n.as_str()).unwrap_or(\"\");\n        if name == \"ask\" || desired_names.contains(name) || !is_skill_command_shape(cmd) {\n            continue;\n        }\n        let Some(id) = cmd.get(\"id\").and_then(|i| i.as_str()) else {\n            continue;\n        };\n        let del = client\n            .delete(format!(\"{base}/{id}\"))\n            .header(\"Authorization\", auth)\n            .send()\n            .await\n            .map_err(reqwest::Error::without_url)?;\n        if del.status().is_success() || del.status() == reqwest::StatusCode::NOT_FOUND {","sourceCodeStart":618,"sourceCodeEnd":654,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/discord/slash.rs#L618-L654","documentation":"During slash-command reconcile, listing the application's existing commands (global or per-guild endpoint) returned a non-success status, so the diff against the desired set cannot be computed. A 429 is special-cased earlier into a RateLimited outcome with a persisted cooldown; anything else bails here.","triggerScenarios":"GET /applications/{app_id}/commands (or /guilds/{guild_id}/commands) answers 401 (invalid bot token), 403 (token does not match the application id), or 5xx — after the 429 branch has already been checked.","commonSituations":"Application id and bot token from different Discord apps after regenerating credentials; token revoked; Discord 5xx during the READY-triggered reconcile; invalid guild id snowflakes in guild scope.","solutions":["401 → fix the bot token in channel config","403 → verify the application id matches the token's application (both must come from the same Discord app)","5xx → wait for the next gateway READY; reconcile is retried automatically","In guild scope, check that guild_ids are valid snowflakes the bot can see"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match reconcile_slash_commands(&client, &token, &app_id, &desired, base, scope, &guilds).await {\n    Ok(ReconcileOutcome::RateLimited { until }) => {\n        // cooldown persisted; wait until `until` before the next attempt\n        tokio::time::sleep_until(until).await;\n    }\n    Ok(ReconcileOutcome::Reconciled) => {}\n    Err(e) if e.to_string().contains(\"listing commands failed\") => {\n        // listing failures are credential-level (401/403) or transient (5xx):\n        // check credentials, then let the next READY retry\n        tracing::warn!(\"slash reconcile listing failed: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep application id and bot token from the same Discord application in config","Reconcile runs on gateway READY — transient listing failures self-heal on reconnect","Persist the rate-limit cooldown so a 429 does not re-hammer Discord on restart"],"tags":["discord","slash-commands","reconcile","http-status","application-id"],"backgroundTag":"discord-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}