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

  1. Use one of the listed ids exactly (lowercase, underscores where shown), after trimming whitespace from config values.
  2. For voice-call, rebuild/install a binary compiled with the channel-voice-call feature; the feature-gated branch tells you when it is missing.
  3. Diff the config's channel ids against the supported list in the error message at startup and fail fast before any messaging happens.
  4. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/ca0813a5a35b79eb. Report an issue: GitHub.