{"record":{"id":"7375219c2c43c2ee","repo":"zeroclaw-labs/zeroclaw","slug":"assistant-threads-setstatus-failed","errorCode":null,"errorMessage":"assistant.threads.setStatus failed: {}","messagePattern":"assistant\\.threads\\.setStatus failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-channels/src/slack.rs","lineNumber":1005,"sourceCode":"        status: &str,\n    ) -> anyhow::Result<()> {\n        let body = serde_json::json!({\n            \"channel_id\": target.channel_id,\n            \"thread_ts\": target.thread_ts,\n            \"status\": status,\n        });\n\n        let response = self\n            .http_client()\n            .post(self.slack_api_url(\"assistant.threads.setStatus\"))\n            .bearer_auth(&self.bot_token)\n            .json(&body)\n            .send()\n            .await?\n            .error_for_status()?;\n        let response: serde_json::Value = response.json().await?;\n        if response.get(\"ok\") != Some(&serde_json::Value::Bool(true)) {\n            anyhow::bail!(\n                \"assistant.threads.setStatus failed: {}\",\n                response\n                    .get(\"error\")\n                    .and_then(serde_json::Value::as_str)\n                    .unwrap_or(\"unknown\")\n            );\n        }\n        Ok(())\n    }\n\n    fn http_client(&self) -> reqwest::Client {\n        zeroclaw_config::schema::build_channel_proxy_client_with_timeouts(\n            \"channel.slack\",\n            self.proxy_url.as_deref(),\n            30,\n            10,\n        )\n    }","sourceCodeStart":987,"sourceCodeEnd":1023,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/slack.rs#L987-L1023","documentation":"Raised when the Slack assistant.threads.setStatus call returns ok != true; {err} is Slack's error string ('invalid_auth' if the token lacks the assistant scope, thread-related errors when the thread is unknown, etc.). setStatus only updates the visual 'thinking/typing' status badge on an assistant thread, so this failure is cosmetic: the actual message content is delivered by separate calls and is unaffected.","triggerScenarios":"A draft/status update path POSTs assistant.threads.setStatus with the thread's channel+ts and a status string; Slack answers ok=false — most often because the app token does not include the assistant thread scope, the thread ts is stale/invalid, or the bot lost access to the channel.","commonSituations":"Slack app manifest missing assistant scopes after adding the channel integration later; status update racing the user closing the thread; bot removed from the channel mid-conversation.","solutions":["Treat as non-fatal: log and continue — the status badge is decorative and the reply still sends","'invalid_auth'-family codes: add the required assistant bot token scopes and reinstall the app","Thread/channel errors: verify the bot is still in the channel and the ts refers to a live thread"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(err) = channel.set_thread_status(channel_id, thread_ts, status).await {\n    ::zeroclaw_log::record!(WARN, ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note), format!(\"assistant status update failed (non-fatal): {err:#}\"));\n    // continue — status badge is cosmetic; the reply itself still sends\n}\nOk(())","preventionTips":["Never fail the message pipeline on setStatus errors — the status badge is decorative","Declare the assistant scopes in the Slack app manifest up front so invalid_auth never occurs","Log these at WARN and move on; only investigate when they repeat for every thread"],"tags":["slack","assistant-threads","set-status","web-api","cosmetic","scopes"],"backgroundTag":"slack-web-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}