{"record":{"id":"bfd1567cb56a198b","repo":"zeroclaw-labs/zeroclaw","slug":"nextcloud-talk-no-bot-secret-configured-set-bot","errorCode":null,"errorMessage":"Nextcloud Talk: no bot secret configured (set bot_token or webhook_secret); refusing to send an unsigned request","messagePattern":"Nextcloud Talk: no bot secret configured \\(set bot_token or webhook_secret\\); refusing to send an unsigned request","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/nextcloud_talk.rs","lineNumber":496,"sourceCode":"    /// Generate a signed Nextcloud Talk bot API request. Fails closed\n    /// (returns `Err`, sends nothing) when no bot secret is configured —\n    /// a missing secret must never be papered over with a fabricated or\n    /// zero-key signature, since that produces an authenticated-looking\n    /// request that can trip Nextcloud's unauthenticated-request throttling.\n    ///\n    /// Bots authenticate via HMAC-SHA256 over `random + message` (the bare\n    /// message text, matching the Nextcloud controller's verification,\n    /// which binds the `message` request parameter — not the raw JSON\n    /// request body), rendered as a bare hex digest (no `sha256=` prefix).\n    /// See: https://nextcloud-talk.readthedocs.io/en/latest/bots/\n    fn create_signed_request(\n        &self,\n        method: Method,\n        url: &str,\n        message: &str,\n    ) -> anyhow::Result<reqwest::RequestBuilder> {\n        let Some(secret) = &self.bot_token else {\n            anyhow::bail!(\n                \"Nextcloud Talk: no bot secret configured (set bot_token or webhook_secret); refusing to send an unsigned request\"\n            );\n        };\n\n        let random = Uuid::new_v4().to_string();\n        let payload = format!(\"{random}{message}\");\n        let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).map_err(|_| {\n            anyhow::Error::msg(\"Nextcloud Talk: failed to create HMAC from bot secret\")\n        })?;\n        mac.update(payload.as_bytes());\n        let signature = hex::encode(mac.finalize().into_bytes());\n\n        Ok(self\n            .client\n            .request(method, url)\n            .header(\"X-Nextcloud-Talk-Bot-Random\", random)\n            .header(\"X-Nextcloud-Talk-Bot-Signature\", signature)\n            .header(\"OCS-APIRequest\", \"true\")","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/nextcloud_talk.rs#L478-L514","documentation":"Nextcloud Talk bots must sign every request with HMAC-SHA256 over `random + message` using the bot secret, sent as `X-Nextcloud-Talk-Bot-Random`/`-Signature` headers. `create_signed_request` refuses to build an unsigned request when `bot_token`/`webhook_secret` is unset, because the server would reject and throttle it — the code treats a missing secret as a hard configuration error.","triggerScenarios":"Calling `send`/`post_to_room` (including draft/finalize flows) on a Nextcloud Talk channel configured without `bot_token` or `webhook_secret` — e.g. only a webhook URL was supplied.","commonSituations":"Setup copied the webhook URL but not the secret shown at bot creation; secret stored under a differently named config key; config edit dropped the token line.","solutions":["Set `bot_token` (or `webhook_secret`) to the secret displayed when the bot was registered in Nextcloud Talk","If the secret was lost, remove and re-add the bot in Talk settings to get a new one","Re-check the channel config keys against the Nextcloud Talk channel docs"],"exampleFix":"# before\n[channels.nextcloud_talk.bot]\nbase_url = \"https://cloud.example.com\"\nwebhook_url = \"https://cloud.example.com/...\"\n\n# after\n[channels.nextcloud_talk.bot]\nbase_url = \"https://cloud.example.com\"\nwebhook_url = \"https://cloud.example.com/...\"\nbot_token = \"<secret shown at bot creation>\"","handlingStrategy":"validation","validationCode":"if talk_config\n    .bot_token\n    .as_deref()\n    .is_none_or(|t| t.trim().is_empty())\n{\n    return Err(anyhow::anyhow!(\n        \"nextcloud_talk requires bot_token/webhook_secret; refusing unsigned sends\"\n    ));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail config validation at startup when a nextcloud_talk channel has no bot_token/webhook_secret","Store the bot secret alongside the webhook URL from day one — both are shown at bot creation","Treat signing capability as a hard prerequisite, never optional, for Talk channels"],"tags":["nextcloud-talk","config","credentials","hmac-signing","rust"],"backgroundTag":"missing-credentials","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}