zeroclaw-labs/zeroclaw · error
Telegram sendMessage (approval) failed after fallback
Error message
Telegram sendMessage (approval) failed after fallback
What it means
Defensive backstop at the end of the Telegram approval send flow: it fires when the send_ok flag is still false after the HTML sendMessage and the plain-text fallback both failed. In the current code every failure branch above either bails with the more specific 'Telegram sendMessage (approval) failed ({status}): {err}' message (error 280) or returns the reqwest transport error directly, so this branch is effectively unreachable — every path that sets nothing returns true. Seeing this message alone means the failure fell through the specific branches without capturing a status.
Source
Thrown at crates/zeroclaw-channels/src/telegram.rs:4699
let err = r.text().await.unwrap_or_default();
self.pending_approvals.lock().await.remove(&approval_id);
anyhow::bail!("Telegram sendMessage (approval) failed ({status}): {err}");
}
Err(e) => {
self.pending_approvals.lock().await.remove(&approval_id);
return Err(e.into());
}
}
}
Err(e) => {
self.pending_approvals.lock().await.remove(&approval_id);
return Err(e.into());
}
};
if !send_ok {
self.pending_approvals.lock().await.remove(&approval_id);
anyhow::bail!("Telegram sendMessage (approval) failed after fallback");
}
// Wait for the user to tap a button. Timeout is configurable via
// `channels.telegram.approval_timeout_secs` (default 120s).
let result =
match tokio::time::timeout(Duration::from_secs(self.approval_timeout_secs), rx).await {
Ok(Ok(response)) => Some(
zeroclaw_api::channel::AttributedApprovalResponse::operator(response),
),
Ok(Err(_)) => {
// Sender dropped — clean up and deny. Nobody tapped.
self.pending_approvals.lock().await.remove(&approval_id);
Some(
zeroclaw_api::channel::AttributedApprovalResponse::from_runtime(
ChannelApprovalResponse::Deny,
zeroclaw_api::channel::ApprovalSource::Unreachable,
),
)View on GitHub (pinned to 88bb9c8533)
Solutions
- Treat it exactly like error 280: check bot token validity, chat_id, and rate limits
- Grep the logs for the preceding WARN 'Telegram sendMessage (approval) with HTML failed' — it carries the actual status and body of the first attempt
- If you hit this exact message with no preceding specific error, report it as a control-flow bug in the approval send path in crates/zeroclaw-channels/src/telegram.rs
Defensive patterns
Strategy: try-catch
Try / catch
// Same handling as the specific sendMessage failure: catch, inspect,
// and deny the pending tool call so the agent does not hang.
if let Err(e) = approval_request.await {
tracing::warn!("approval delivery failed: {e}; defaulting to deny");
return ChannelApprovalResponse::Deny;
} Prevention
- Treat any approval-delivery error as 'deny by default' so tools never wait forever
- Keep bot token and chat_id validated at startup (see error 280)
- If this exact message appears without a preceding specific error, file a bug — the branch should be unreachable
When it happens
Trigger: Both the HTML-formatted and plain-text sendMessage attempts for an approval prompt fail without a transport error, and neither specific bail branch fires. Practically unreachable today: the fallback's non-success branch bails with the detailed error 280 first, and transport errors return early.
Common situations: Same conditions as error 280 (bad token, unknown chat, rate limit) persisting across both attempts; or a future refactor that breaks the match chain so failures fall through with send_ok false.
Related errors
- Telegram sendMessage (approval) failed ({status}): {err}
- {method} failed: status={status}, body={body}
- Telegram file download failed: {}
- {method} by URL failed: {err}
- Telegram sendDocument by URL failed: {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/a23e9714576835e5.
Report an issue: GitHub.