zeroclaw-labs/zeroclaw · warning · DiagItem

channels.discord.{alias}.bot_token is unset but the channel

Error message

channels.discord.{alias}.bot_token is unset but the channel is enabled — the channel cannot connect until a bot token is set

What it means

A `zeroclaw doctor` warning from `check_config_semantics`: the Discord twin of the Telegram token check — a Discord alias has `enabled = true` while its `bot_token` is an unset display value. The serde default on `bot_token` means the partial alias loads without entering `degraded_sections`, so doctor names the unset field before the channel fails to connect.

Source

Thrown at crates/zeroclaw-runtime/src/doctor/mod.rs:1349

    }

    // Enabled bot channels with no token: a partial alias survives the
    // resilient load (`bot_token` has a serde default), so it never
    // reaches `degraded_sections` — doctor must name the unset field here
    // or the operator only finds out when the channel fails to start.
    for (alias, tg) in &cc.telegram {
        if tg.enabled && zeroclaw_config::traits::is_unset_display_value(&tg.bot_token) {
            items.push(DiagItem::warn(
                cat,
                format!(
                    "channels.telegram.{alias}.bot_token is unset but the channel is enabled — the channel cannot connect until a bot token is set"
                ),
            ));
        }
    }
    for (alias, dc) in &cc.discord {
        if dc.enabled && zeroclaw_config::traits::is_unset_display_value(&dc.bot_token) {
            items.push(DiagItem::warn(
                cat,
                format!(
                    "channels.discord.{alias}.bot_token is unset but the channel is enabled — the channel cannot connect until a bot token is set"
                ),
            ));
        }
    }

    // Delegate agents: model_provider validity (resolved from model_provider alias)
    let mut agent_names: Vec<_> = config.agents.keys().collect();
    agent_names.sort();
    for name in agent_names {
        let agent = config.agents.get(name).unwrap();
        let provider_ref = agent.model_provider.as_str();
        if provider_ref.is_empty() {
            continue;
        }
        if let Some(reason) = provider_validation_error(config, provider_ref) {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set a real token from the Discord developer portal in `channels.discord.<alias>.bot_token`, or export the env var it references where the daemon runs.
  2. For systemd, wire `EnvironmentFile=` into the unit so the placeholder resolves at start.
  3. Set `enabled = false` if the channel should not connect yet.
  4. Re-run `zeroclaw doctor` to confirm the warning cleared.

Example fix

# before
[channels.discord.default]
enabled = true
bot_token = ""

# after
[channels.discord.default]
enabled = true
bot_token = "MTIzNDU2Nzg-your-discord-token"
Defensive patterns

Strategy: validation

Validate before calling

for (alias, dc) in &config.channels.discord {
    if dc.enabled && zeroclaw_config::traits::is_unset_display_value(&dc.bot_token) {
        eprintln!(
            "channels.discord.{alias}.bot_token unset — set the token or disable the channel"
        );
    }
}

Type guard

fn channel_ready(enabled: bool, bot_token: &str) -> bool {
    !enabled || !zeroclaw_config::traits::is_unset_display_value(bot_token)
}

Prevention

When it happens

Trigger: Running `zeroclaw doctor` when `channels.discord.<alias>` sets `enabled = true` but `bot_token` is empty or an unresolved `${...}` placeholder (`zeroclaw_config::traits::is_unset_display_value`).

Common situations: Discord bot token from the developer portal never pasted into config; env-var-indirected token missing from the systemd unit or container env; enabling the Discord alias before finishing setup.

Related errors


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