zeroclaw-labs/zeroclaw · error · anyhow::Error
heartbeat.target is required when heartbeat.to is set
Error message
heartbeat.target is required when heartbeat.to is set
What it means
resolve_heartbeat_delivery requires heartbeat.target and heartbeat.to to be set together. A recipient without a channel cannot be routed, so setting to while target is unset bails at heartbeat worker startup instead of guessing a channel for an explicitly given recipient.
Source
Thrown at crates/zeroclaw-runtime/src/daemon/mod.rs:2211
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
let target = config
.heartbeat
.to
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
match (channel, target) {
// Both explicitly set — validate and use.
(Some(channel), Some(target)) => {
validate_heartbeat_channel_config(config, channel)?;
Ok(Some((channel.to_string(), target.to_string())))
}
// Only one set — error.
(Some(_), None) => anyhow::bail!("heartbeat.to is required when heartbeat.target is set"),
(None, Some(_)) => anyhow::bail!("heartbeat.target is required when heartbeat.to is set"),
// Neither set — try auto-detect the first configured channel.
(None, None) => Ok(auto_detect_heartbeat_channel(config)),
}
}
const HEARTBEAT_SESSION_CONTEXT_MESSAGES: usize = 20;
fn load_heartbeat_session_context(config: &Config) -> Option<String> {
use zeroclaw_providers::traits::ChatMessage;
let channel = config
.heartbeat
.target
.as_deref()
.map(str::trim)
.filter(|v| !v.is_empty())?;
let to = config
.heartbeatView on GitHub (pinned to 88bb9c8533)
Solutions
- Add target = "<channel>" next to to in the [heartbeat] table
- Or remove both keys to fall back to auto-detection of the first configured channel
Example fix
# before [heartbeat] enabled = true agent = "main" to = "123456789" # after [heartbeat] enabled = true agent = "main" target = "telegram" to = "123456789"
Defensive patterns
Strategy: validation
Validate before calling
match (&config.heartbeat.target, &config.heartbeat.to) {
(Some(_), None) => anyhow::bail!("heartbeat.to is required when heartbeat.target is set"),
(None, Some(_)) => anyhow::bail!("heartbeat.target is required when heartbeat.to is set"),
_ => {}
} Type guard
fn heartbeat_delivery_pair_is_complete(config: &zeroclaw_config::schema::Config) -> bool {
config.heartbeat.target.is_some() == config.heartbeat.to.is_some()
} Try / catch
if let Err(err) = run_heartbeat_worker(config.clone()).await {
if err.to_string().contains("heartbeat.target is required") {
eprintln!("config error: add heartbeat.target (channel) alongside heartbeat.to");
}
return Err(err);
} Prevention
- Do not comment out only one of target/to when debugging delivery — remove both or keep both
- The legacy aliases channel/recipient map to the same pair and obey the same rule
When it happens
Trigger: [heartbeat] to = "123456789" with no target key; or the legacy alias recipient = "..." without channel.
Common situations: Configuring the recipient first and intending to add the channel later; commenting out the target line while debugging delivery.
Understand the failure class
Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.
Related errors
- heartbeat.to is required when heartbeat.target is set
- unsupported heartbeat.target channel: {channel}
- heartbeat worker requires `[heartbeat] agent = "<alias>"` na
- heartbeat.target is set to {channel} but channels.{channel}
- heartbeat.target is set to {channel} but {channel} is an inp
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b9eb06d8537cfbb8.
Report an issue: GitHub.