zeroclaw-labs/zeroclaw · error · anyhow::Error

peer group [{conventional_key}] already exists but its chann

Error message

peer group [{conventional_key}] already exists but its channel ref is `{}` (expected `{dotted_ref}`) — fix the group key or channel ref in config.toml before pairing

What it means

When no existing peer group carries this channel's dotted ref, merge_external_peer wants to create the conventional '<channel_type>_<alias>' key with channel = '<channel_type>.<alias>'. If that exact key is already taken by a group whose channel field points at a different ref, it bails rather than overwriting: writing there would store the identity where this channel's reader (Config::channel_external_peers, which matches on the channel field) never looks, while the other channel's reader would silently start authorizing it.

Source

Thrown at crates/zeroclaw-channels/src/identity_persist.rs:125

    };

    if let Some(key) = target_key {
        // Invariant: `target_key` was selected from existing map entries.
        if let Some(group) = cfg.peer_groups.get_mut(&key) {
            group
                .external_peers
                .push(PeerUsername::new(normalized.to_string()));
        }
        return Ok(true);
    }

    // No group carries this channel's dotted ref yet — create the
    // conventional shape. Refuse to squat on a key that belongs to a
    // different channel: writing there would put the identity where this
    // channel's reader never looks, while the *other* channel's reader
    // would silently start authorizing it.
    if let Some(existing) = cfg.peer_groups.get(&conventional_key) {
        anyhow::bail!(
            "peer group [{conventional_key}] already exists but its channel ref \
             is `{}` (expected `{dotted_ref}`) — fix the group key or channel ref \
             in config.toml before pairing",
            existing.channel.as_str()
        );
    }
    cfg.peer_groups.insert(
        conventional_key,
        PeerGroupConfig {
            channel: ChannelRef::new(dotted_ref),
            external_peers: vec![PeerUsername::new(normalized.to_string())],
            ..PeerGroupConfig::default()
        },
    );
    Ok(true)
}

/// Persist a paired identity as an authorized external peer.

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Rename the colliding group key in config.toml to something that does not equal <channel_type>_<alias> (e.g. telegram_main), keeping its channel field as-is
  2. Or, if the existing group was actually meant for this channel, fix its channel field to the expected dotted ref '<channel_type>.<alias>' shown in the error
  3. Or delete the stale group if it authorizes nothing, letting pairing recreate it in the conventional shape
  4. Re-run the pairing after the config edit — the merge is idempotent and will now find a clean path

Example fix

# before (config.toml)
[peer_groups.wechat_main]
channel = "telegram.main"

# after — rename the key so it no longer collides with the conventional wechat slot
[peer_groups.telegram_main]
channel = "telegram.main"
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight before pairing: detect the conventional-key collision merge would reject:
let conventional = format!("{channel_type}_{alias}");
if let Some(g) = cfg.peer_groups.get(&conventional) {
    anyhow::ensure!(
        g.channel.as_str() == format!("{channel_type}.{alias}"),
        "peer_groups[{conventional}] collides: channel={}", g.channel.as_str()
    );
}

Try / catch

match merge_external_peer(&mut cfg, channel_type, alias, identity) {
    Err(e) if e.to_string().contains("already exists but its channel ref") => {
        // surface exact expected/actual refs to the operator; block pairing until config fixed
    }
    other => other?,
}

Prevention

When it happens

Trigger: config.toml contains e.g. [peer_groups.wechat_main] with channel = "telegram.main" (hand-named group that collides with the conventional wechat key), and a WeChat pairing for alias 'main' then needs to create its conventional group. The bail reports the existing group's actual channel ref versus the expected dotted ref.

Common situations: Operator hand-rolled peer groups before the conventional naming existed; copy-pasted a group block and changed the channel field but not the key; two channel types whose names concatenate to the same conventional key.

Related errors


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