zeroclaw-labs/zeroclaw · error · anyhow::Error
approval route channel '{channel_key}' is not a configured c
Error message
approval route channel '{channel_key}' is not a configured channel (route '{route}') What it means
The approval route parsed fine, but its channel half names a channel key that is not present in the runtime's channel registry (self.channels). A misconfigured route is treated as a real operator error: deliver() returns Err so the broker logs it. It never affects the gate itself — the broker's deliver wrappers only log.
Source
Thrown at crates/zeroclaw-runtime/src/sop/approval/channel_route.rs:377
);
};
let msg = SendMessage::new(render_notice(kind, notice), recipient).suppress_voice();
Ok((channel_key.to_string(), msg))
}
impl ApprovalRouteAdapter for ChannelRouteAdapter {
fn deliver(
&self,
kind: ApprovalNoticeKind,
route: &str,
notice: &GateNotice<'_>,
) -> anyhow::Result<()> {
let (channel_key, msg) = build_delivery(kind, route, notice)?;
let Some(channel) = self.channels.get(&channel_key).cloned() else {
// A misconfigured route (names a channel that isn't configured) is a real
// operator error worth surfacing: return Err so the broker logs it. It
// still never affects the gate (the broker's deliver_* wrappers only log).
anyhow::bail!(
"approval route channel '{channel_key}' is not a configured channel \
(route '{route}')"
);
};
// An inbound-only channel's `send` is a no-op that returns `Ok`, so spawning it
// would report success without delivering anything. Refuse and surface it (the
// broker logs the Err) rather than silently dropping the notice.
if !channel.supports_outbound_send() {
anyhow::bail!(
"approval route channel '{channel_key}' does not support outbound \
delivery (it is inbound-only); its approval notice cannot be sent \
(route '{route}')"
);
}
// Fire-and-forget: hand the async send to the runtime and return. The gate is
// never blocked on channel I/O; a send failure is logged in the task.
// Native gate prompt first (buttons / keyboards, answered through the
// channel's inbound path); channels without one fall back to the textView on GitHub (pinned to 88bb9c8533)
Solutions
- Fix the route's channel half to exactly match a configured channel key (list your channels config).
- Or add/enable the channel under the key the route names.
- Keep route and channel keys in one place — derive route strings from channel keys rather than retyping them.
- Add a config lint that cross-checks every approval route channel against configured channel keys at startup.
Example fix
# before [channels.discord] token = "..." [approval] route = "discord.ops:123456789" # no channel keyed 'discord.ops' # after [channels.discord.ops] token = "..." [approval] route = "discord.ops:123456789"
Defensive patterns
Strategy: validation
Validate before calling
let (channel_key, _) = route.split_once(':').expect("validated route");
assert!(
channels.contains_key(channel_key),
"approval route channel '{channel_key}' is not configured"
); Type guard
fn route_channel_configured(route: &str, channels: &ChannelMap) -> bool {
route.split_once(':').map(|(c, _)| channels.contains_key(c)).unwrap_or(false)
} Try / catch
match router.deliver(kind, route, ¬ice).await {
Err(e) if e.to_string().contains("is not a configured channel") => {
log::warn!("operator error: route {route} names an unconfigured channel; fix config");
}
rest => rest?,
} Prevention
- Cross-check approval route channel halves against configured channel keys at config load time.
- When renaming/removing a channel, grep config for routes referencing it in the same change.
- The broker only logs this error — add alerting on deliver failures so misroutes are noticed.
When it happens
Trigger: deliver() with a route like "discord.ops:123" when no channel keyed "discord.ops" is configured — e.g. the channel section was renamed, disabled, or the route was copied from another deployment.
Common situations: Channel renamed in config but approval route left stale; channel section commented out for testing; environment-specific channel keys (discord-prod vs discord-ops) mismatched; typo in the channel half of the route.
Related errors
- approval route '{route}' is not 'channel:recipient' (e.g. 'd
- approval route channel '{channel_key}' does not support outb
- discord send has no recipient: message.recipient is empty an
- Telegram sendMessage (approval) failed ({status}): {err}
- unsupported room visibility '{other}': expected private or p
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/821d3d9758f10812.
Report an issue: GitHub.