zeroclaw-labs/zeroclaw · error · anyhow::Error

Talk API error: {status}

Error message

Talk API error: {status}

What it means

`send_to_room` posts an HMAC-signed message to the Talk bot API and bails with `Talk API error: {status}` on any non-success response; the status and body are also logged at ERROR level with the module's structured logger. Common statuses: 401 bad/mismatched bot secret, 404 unknown room token, 400 rejected message.

Source

Thrown at crates/zeroclaw-channels/src/nextcloud_talk.rs:549

    }

    async fn send_to_room(&self, room_token: &str, content: &str) -> anyhow::Result<()> {
        let response = self.post_to_room(room_token, content).await?;

        if response.status().is_success() {
            return Ok(());
        }

        let status = response.status();
        let body = response.text().await.unwrap_or_default();
        ::zeroclaw_log::record!(
            ERROR,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                .with_attrs(::serde_json::json!({"status": status.to_string(), "body": body})),
            "Talk send failed:"
        );
        anyhow::bail!("Talk API error: {status}");
    }

    /// Send a message and return the response from Nextcloud Talk.
    async fn send_to_room_with_id(
        &self,
        room_token: &str,
        content: &str,
    ) -> anyhow::Result<String> {
        let response = self.post_to_room(room_token, content).await?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                    .with_outcome(::zeroclaw_log::EventOutcome::Unknown)
                    .with_attrs(::serde_json::json!({"status": status.to_string(), "body": body})),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Check the structured log entry — it includes the status and the response body, which states the reason
  2. 401: re-copy the current bot secret into `bot_token`
  3. 404: verify the room token matches the conversation the bot is in
  4. Ensure the bot is still a participant of the target room
Defensive patterns

Strategy: try-catch

Try / catch

match talk_channel.send(msg).await {
    Err(e) if e.to_string().starts_with("Talk API error: 401") => {
        // secret mismatch: re-copy bot secret, then retry once
    }
    Err(e) if e.to_string().starts_with("Talk API error: 404") => {
        // wrong room token: fix mapping, do not retry
    }
    other => other,
}

Prevention

When it happens

Trigger: `Channel::send` (or `finalize_draft`) posting to a room where signing fails validation (stale secret), the bot was removed from the conversation, or the room token is wrong.

Common situations: Bot secret regenerated in Nextcloud after config was written; bot kicked from the conversation; room token copied from a share link instead of the API token; message over Talk's length limit after truncation rules.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/78a719a026a0be26. Report an issue: GitHub.