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
- 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
- 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
- Or delete the stale group if it authorizes nothing, letting pairing recreate it in the conventional shape
- 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
- Adopt the conventional <type>_<alias> key / channel = "<type>.<alias>" shape for hand-written peer groups
- Run a config-lint rule: no peer group key may equal a conventional key of a different channel's dotted ref
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
- Missing [channels.{channel_type}.{alias}] section in config.
- email channel '{}' has oauth2 configured but no auth service
- git channel: reading private_key_path `{path}` failed: {e}
- git channel provider `{provider}` requires channels.git.<ali
- unknown git channel provider `{other}` (supported: github, g
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b801366591ba1a37.
Report an issue: GitHub.