zeroclaw-labs/zeroclaw · error · anyhow::Error
matrix: not a room id or alias: {id_or_alias}
Error message
matrix: not a room id or alias: {id_or_alias} What it means
resolve_room accepts exactly two recipient shapes: room ids starting with '!' (parsed locally) and room aliases starting with '#' (resolved via the homeserver behind a cache). Anything else - user ids starting with '@', bare room names, URLs - is rejected up front. Note normalize_recipient first strips a '||'-suffixed form with a warning, so the value echoed in the error is the bare recipient that failed the prefix check.
Source
Thrown at crates/zeroclaw-channels/src/matrix.rs:1955
id_or_alias: &str,
) -> Result<OwnedRoomId> {
let (id_or_alias, normalized) = normalize_recipient(id_or_alias);
if normalized {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
.with_outcome(::zeroclaw_log::EventOutcome::Unknown)
.with_attrs(::serde_json::json!({"id_or_alias": id_or_alias})),
"matrix: recipient contains `||`; using as the room target. Update channels.matrix or cron `delivery.to` to a plain room id/alias to silence this warning."
);
}
if id_or_alias.starts_with('!') {
return id_or_alias
.parse::<matrix_sdk::ruma::OwnedRoomId>()
.with_context(|| format!("parse room id {id_or_alias}"));
}
if !id_or_alias.starts_with('#') {
bail!("matrix: not a room id or alias: {id_or_alias}");
}
if let Some(id) = cache.read().await.get(id_or_alias) {
return Ok(id.clone());
}
let alias: &RoomAliasId = id_or_alias
.try_into()
.with_context(|| format!("parse room alias {id_or_alias}"))?;
let resp = client
.resolve_room_alias(alias)
.await
.with_context(|| format!("resolve room alias {id_or_alias}"))?;
cache
.write()
.await
.insert(id_or_alias.to_string(), resp.room_id.clone());
Ok(resp.room_id)
}
}View on GitHub (pinned to 88bb9c8533)
Solutions
- Use the full alias form #room:example.org or the opaque room id form !xxxx:example.org.
- Copy the canonical value from a Matrix client's room settings (room address or internal room id).
- Fix the config key that produced the bad recipient - it is echoed verbatim in the error.
- If you meant to DM a user, create or join a room with them first and use that room's id or alias.
Example fix
# before: cron delivery target is a user id [cron.check-in] delivery.to = "@alice:example.org" # after: use a room alias (or room id) delivery.to = "#zeroclaw-ops:example.org"
Defensive patterns
Strategy: validation
Validate before calling
fn is_matrix_room_ref(recipient: &str) -> bool {
let s = recipient.trim();
s.starts_with('!') || s.starts_with('#')
}
for peer in peer_list {
assert!(is_matrix_room_ref(&peer), "recipient {peer} is not a room id (!...) or alias (#room:server)");
} Type guard
fn as_room_id_or_alias(s: &str) -> Option<&str> {
let t = s.trim();
(t.starts_with('!') || t.starts_with('#')).then_some(t)
} Prevention
- Canonicalize every configured peer and cron delivery target to a full alias with the homeserver suffix at config-load time.
- Reject '@'-prefixed values in config lint - DM-style user ids are the classic copy-paste mistake.
- Log the normalized recipient list at startup so the exact resolution inputs are reviewable.
When it happens
Trigger: Calling any send or resolve path with a recipient that starts with neither '!' nor '#': '@user:example.org' (a user id), 'ops-room' (bare name), or a pasted URL - sourced from channels.matrix config, cron delivery.to, or an upstream message's channel field.
Common situations: Confusing a Matrix user id with a room id; pasting a room's display name instead of its canonical alias; cron or delivery config holding '#ops' without the homeserver suffix; peer list entries that were never canonicalized.
Related errors
- matrix: draft message id is empty
- matrix: `homeserver` is required
- Recipient cannot be empty
- Recipient `{trimmed}` does not contain a valid phone number
- modal custom_id exceeds Discord's 100-char limit; cannot ope
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/a32079661b37ed83.
Report an issue: GitHub.