zeroclaw-labs/zeroclaw · warning · DiagItem
{channel_count} channels, {stale} stale
Error message
{channel_count} channels, {stale} stale What it means
A `zeroclaw doctor` warning from `check_daemon_state`: a summary line emitted when at least one tracked channel component is stale. Each stale channel already produced its own error item (`<name> stale (ok=..., age=...)`); this warning aggregates the count (`{channel_count} channels, {stale} stale`) so the overall channel health is visible at a glance. Stale means status not `ok` or `last_ok` older than `CHANNEL_STALE_SECONDS`.
Source
Thrown at crates/zeroclaw-runtime/src/doctor/mod.rs:1708
.map_or(i64::MAX, |dt| {
Utc::now().signed_duration_since(dt).num_seconds()
});
if status_ok && age <= CHANNEL_STALE_SECONDS {
items.push(DiagItem::ok(cat, format!("{name} fresh ({age}s ago)")));
} else {
stale += 1;
items.push(DiagItem::error(
cat,
format!("{name} stale (ok={status_ok}, age={age}s)"),
));
}
}
if channel_count == 0 {
items.push(DiagItem::warn(cat, "no channel components tracked yet"));
} else if stale > 0 {
items.push(DiagItem::warn(
cat,
format!("{channel_count} channels, {stale} stale"),
));
}
}
}
// ── Environment checks ───────────────────────────────────────────
fn check_environment(items: &mut Vec<DiagItem>) {
let cat = "environment";
// git
check_command_available("git", &["--version"], cat, items);
// Shell — Unix uses $SHELL, Windows uses %ComSpec% (path to cmd.exe).
let shell = std::env::var("SHELL")
.ok()View on GitHub (pinned to 88bb9c8533)
Solutions
- Look at the per-channel `stale` error items in the same doctor report to identify which channels are unhealthy.
- Check daemon logs (`journalctl --user -u zeroclaw`) for the failing channel's reconnect errors.
- Restart the daemon (or the affected channel) to re-establish connections and refresh `last_ok`.
- Re-run `zeroclaw doctor` and confirm the stale count drops to zero.
Example fix
# before: doctor shows "3 channels, 2 stale" plus per-channel stale errors # after journalctl --user -u zeroclaw -n 200 --no-pager # find the failing channel systemctl --user restart zeroclaw && sleep 15 && zeroclaw doctor
Defensive patterns
Strategy: validation
Validate before calling
let now = chrono::Utc::now();
if let Some(components) = state.get("components").and_then(|c| c.as_object()) {
for (name, c) in components {
if !name.starts_with("channel:") { continue; }
let fresh = c.get("last_ok")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map_or(false, |t| (now - t.with_timezone(&chrono::Utc)).num_seconds() <= 300);
if !fresh { eprintln!("{name} stale"); }
}
} Prevention
- Monitor channel `last_ok` ages continuously instead of discovering staleness via doctor.
- Alert on reconnect-loop patterns in daemon logs before channels go stale.
When it happens
Trigger: Running `zeroclaw doctor` when one or more `channel:*` components in the daemon state file report a non-ok status or a `last_ok` timestamp older than the channel staleness threshold — e.g. a Telegram connection dropped, or the daemon suspended and resumed.
Common situations: Network outage killing long-poll connections; expired/revoked bot token causing reconnect loops; laptop resumed from suspend leaving timestamps old; daemon overloaded so heartbeats lag.
Related errors
- no channel components tracked yet
- scheduler component not tracked yet
- no channels configured — run `zeroclaw quickstart` to set on
- channels.telegram.{alias}.bot_token is unset but the channel
- channels.discord.{alias}.bot_token is unset but the channel
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/fe8b17cf4b6b2f7b.
Report an issue: GitHub.