zeroclaw-labs/zeroclaw · error · UnknownChannelId
Unknown channel '{channel_id}'. Supported: telegram, discord
Error message
Unknown channel '{channel_id}'. Supported: telegram, discord, slack, mattermost, signal, matrix, whatsapp, qq, lark, feishu, dingtalk, wecom, wecom_ws, nextcloud_talk, linq, email, gmail_push, git, irc, twitter, mochat, imessage, line, voice-call What it means
The channel orchestrator maps a channel id string to its builder via a match over the supported set. Any id outside the vocabulary (telegram, discord, slack, mattermost, signal, matrix, whatsapp, qq, lark, feishu, dingtalk, wecom, wecom_ws, nextcloud_talk, linq, email, gmail_push, git, irc, twitter, mochat, imessage, line, voice-call) is wrapped in UnknownChannelId. Feature-gated channels also produce a distinct bail! when compiled without their feature.
Source
Thrown at crates/zeroclaw-channels/src/orchestrator/mod.rs:9674
}
}
"voice-call" => {
#[cfg(feature = "channel-voice-call")]
{
let (alias, vc) = config
.channels
.voice_call
.iter()
.next()
.context("Voice Call channel is not configured")?;
Ok(Arc::new(VoiceCallChannel::new(alias.clone(), vc.clone())))
}
#[cfg(not(feature = "channel-voice-call"))]
{
anyhow::bail!("Voice Call channel requires the `channel-voice-call` feature");
}
}
other => Err(anyhow::Error::new(UnknownChannelId(other.to_string()))),
}
}
/// Send a one-off message to a configured channel.
pub async fn send_channel_message(
config: &Config,
channel_id: &str,
recipient: &str,
message: &str,
) -> Result<()> {
// Wrap into the canonical shared handle for the builder; this is a
// one-shot path so the snapshot is dropped immediately after send.
let config_arc = Arc::new(RwLock::new(config.clone()));
// The builder gets first refusal so families it already resolves natively
// (notably `linq.<alias>`) keep their established route and their own
// configuration errors. Only a dotted id the builder does not claim at all
// falls through to the announcement dispatcher, which resolves any
// `<type>.<alias>` it supports.View on GitHub (pinned to 88bb9c8533)
Solutions
- Use one of the listed ids exactly (lowercase, underscores where shown), after trimming whitespace from config values.
- For voice-call, rebuild/install a binary compiled with the channel-voice-call feature; the feature-gated branch tells you when it is missing.
- Diff the config's channel ids against the supported list in the error message at startup and fail fast before any messaging happens.
- If the channel exists upstream but not in your binary, upgrade to a release that includes it.
Example fix
# before
channels:
telgram:
token: ...
# after
channels:
telegram:
token: ... Defensive patterns
Strategy: type-guard
Validate before calling
const SUPPORTED_CHANNELS: &[&str] = &["telegram","discord","slack","mattermost","signal","matrix","whatsapp","qq","lark","feishu","dingtalk","wecom","wecom_ws","nextcloud_talk","linq","email","gmail_push","git","irc","twitter","mochat","imessage","line","voice-call"];
fn assert_supported(channel_id: &str) -> anyhow::Result<()> {
anyhow::ensure!(SUPPORTED_CHANNELS.contains(&channel_id.trim()), "unsupported channel id {channel_id}");
Ok(())
} Type guard
fn is_supported_channel(channel_id: &str) -> bool {
SUPPORTED_CHANNELS.contains(&channel_id.trim())
} Try / catch
match orchestrator::build_channel(config, channel_id).await {
Err(e) if e.downcast_ref::<UnknownChannelId>().is_some() => {
fail_fast_with_supported_list(); // config error: stop startup, list valid ids
}
other => other,
} Prevention
- Validate all channel ids from config against the supported list at startup, before any message flows.
- Trim whitespace and normalize casing when loading channel ids from YAML/TOML.
- Pin the release notes: when adopting new channels, confirm your binary was built with their cargo features.
When it happens
Trigger: Calling the orchestrator's channel construction with a misspelled or unsupported id: 'telgram', 'WebChat', a display name instead of the id, or 'voice-call' in a binary built without the channel-voice-call feature (that path bails separately with the feature message).
Common situations: Typos in channel config files, configs written against a newer ZeroClaw release with new channels then run on an older binary, whitespace/casing drift from YAML, channels whose cargo feature was not enabled at build time.
Related errors
- iMessage channel is not configured
- unsupported room visibility '{other}': expected private or p
- channel '{}' does not support forge API requests
- non-success status {}
- email channel '{}' has oauth2 configured but no auth service
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/ca0813a5a35b79eb.
Report an issue: GitHub.