{"record":{"id":"2bfc4d0d33125aa5","repo":"zeroclaw-labs/zeroclaw","slug":"get-channels-id-returned-explicit-channel-i","errorCode":null,"errorMessage":"GET /channels/{id} returned {}: explicit channel_id is not accessible to this bot","messagePattern":"GET /channels/(.+?) returned (.+?): explicit channel_id is not accessible to this bot","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/mattermost.rs","lineNumber":212,"sourceCode":"            .filter(|id| seen.insert(id.clone()))\n            .collect();\n        if ids.is_empty() { None } else { Some(ids) }\n    }\n\n    pub(crate) async fn list_target_channels(&self) -> Result<Vec<TargetChannel>> {\n        let token = self.token().await?.to_string();\n        if let Some(ids) = self.scoped_channel_ids() {\n            let mut out = Vec::with_capacity(ids.len());\n            for id in ids {\n                let resp = self\n                    .http_client()\n                    .get(format!(\"{}/api/v4/channels/{}\", self.base_url, id))\n                    .bearer_auth(&token)\n                    .send()\n                    .await\n                    .with_context(|| format!(\"GET /channels/{id} failed\"))?;\n                if !resp.status().is_success() {\n                    bail!(\n                        \"GET /channels/{id} returned {}: explicit channel_id is not accessible to this bot\",\n                        resp.status()\n                    );\n                }\n                let body: serde_json::Value = resp\n                    .json()\n                    .await\n                    .with_context(|| format!(\"decode /channels/{id} body\"))?;\n                let ty = body.get(\"type\").and_then(|v| v.as_str()).unwrap_or(\"\");\n                out.push(TargetChannel {\n                    id,\n                    is_direct: is_direct_channel(ty),\n                });\n            }\n            return Ok(out);\n        }\n        let resp = self\n            .http_client()","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/mattermost.rs#L194-L230","documentation":"When `channel_ids` lists explicit IDs, `list_target_channels` resolves each one via `GET /api/v4/channels/{id}` with the bot's bearer token. This error means one lookup returned a non-2xx status: the bot token is not valid (401), the bot is not a member of that (private) channel (403), or the ID does not exist / was deleted (404).","triggerScenarios":"`channels.mattermost.<alias>.channel_ids` contains an inaccessible channel (bot never added, removed, archived, or deleted channel, or a typo'd ID), raised on the first `listen()`, `listen_polling()`, `listen_websocket()`, or direct `list_target_channels()` call.","commonSituations":"Bot was removed from a private channel after config was written; channel ID copied from a different Mattermost instance; bot account exists but was never invited to the team/channel; personal access token revoked.","solutions":["Verify the failing ID: `curl -H \"Authorization: Bearer <token>\" <server>/api/v4/channels/<id>` and check the status","Add the bot to the channel (UI \"Add members\" or `POST /api/v4/channels/{id}/members` with the bot's user ID)","If status is 401, regenerate/fix the bot token first","Replace the explicit ID with `*` (or drop `channel_ids`) to fall back to auto-discovery of channels the bot already sees"],"exampleFix":"# before\n[channels.mattermost.team]\nchannel_ids = [\"abc123notjoined\"]\n\n# after\n[channels.mattermost.team]\nchannel_ids = [\"*\"]  # auto-discover, or list only channels the bot has joined","handlingStrategy":"try-catch","validationCode":"for id in &channel_ids {\n    let resp = client\n        .get(format!(\"{base_url}/api/v4/channels/{id}\"))\n        .bearer_auth(&token)\n        .send()\n        .await?;\n    if !resp.status().is_success() {\n        eprintln!(\"channel {id} not accessible: {}\", resp.status());\n    }\n}","typeGuard":null,"tryCatchPattern":"match mm_channel.listen(tx).await {\n    Err(e) if e.to_string().contains(\"explicit channel_id is not accessible\") => {\n        // surface which channel_ids entry failed, fix membership or switch to \"*\"\n    }\n    other => other,\n}","preventionTips":["Ensure the bot is a member of every channel listed in channel_ids before enabling the channel","Add a startup health probe that resolves each explicit channel ID via the REST API","Prefer auto-discovery (\"*\" or empty channel_ids) unless explicit scoping is required"],"tags":["mattermost","channel-access","http-status","rust"],"backgroundTag":"channel-access-denied","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}