zeroclaw-labs/zeroclaw · error · anyhow::Error
matrix: configured channels.matrix.user-id ({configured}) do
Error message
matrix: configured channels.matrix.user-id ({configured}) does not match Matrix whoami user_id ({}) What it means
For access-token login the channel calls the homeserver's whoami endpoint to learn which user the token actually authenticates, then cross-checks it against the configured channels.matrix.user-id. A mismatch means the token belongs to a different Matrix account than declared - all sends would go out as the wrong user - so login aborts before the bot joins any room.
Source
Thrown at crates/zeroclaw-channels/src/matrix.rs:1633
) -> Result<AccessTokenIdentity> {
let configured_user_id = non_empty_config_value(config.user_id.as_deref());
let configured_device_id = non_empty_config_value(config.device_id.as_deref());
if let (Some(user_id), Some(device_id)) =
(configured_user_id.as_ref(), configured_device_id.as_ref())
{
return Ok(AccessTokenIdentity {
user_id: user_id.clone(),
device_id: Some(device_id.clone()),
});
}
let whoami = fetch_access_token_whoami(config).await?;
if let Some(ref configured) = configured_user_id
&& configured != &whoami.user_id
{
bail!(
"matrix: configured channels.matrix.user-id ({configured}) does not match Matrix whoami user_id ({})",
whoami.user_id
);
}
if let (Some(configured), Some(actual)) = (&configured_device_id, &whoami.device_id)
&& configured != actual
{
bail!(
"matrix: configured channels.matrix.device-id ({configured}) does not match Matrix whoami device_id ({actual})"
);
}
if configured_device_id.is_none() && whoami.device_id.is_none() {
bail!(
"matrix: whoami response did not include device_id; configure channels.matrix.device-id for access-token login"
);
}View on GitHub (pinned to 88bb9c8533)
Solutions
- Ask the homeserver who the token belongs to (whoami) and correct channels.matrix.user-id to that exact id.
- Or mint a new access token for the account named in channels.matrix.user-id and replace the token.
- If pinning the user-id is optional for you, remove it and let login adopt the whoami identity.
- Check the homeserver part of the user-id - @name:server must match the account's home server exactly.
Example fix
# before: token actually belongs to @ops:example.org [channels.matrix] user-id = "@bot:example.org" access-token = "syt_..." # after [channels.matrix] user-id = "@ops:example.org" access-token = "syt_..."
Defensive patterns
Strategy: validation
Validate before calling
async fn token_identity_matches(cfg: &MatrixConfig) -> anyhow::Result<bool> {
let url = format!(
"{}/_matrix/client/v3/account/whoami",
cfg.homeserver.trim_end_matches('/')
);
let who: serde_json::Value = reqwest::Client::new()
.get(url)
.bearer_auth(cfg.access_token.as_deref().unwrap_or_default())
.send().await?
.error_for_status()?
.json().await?;
Ok(who["user_id"].as_str() == cfg.user_id.as_deref())
} Prevention
- Pair each access token with the user-id it was minted for in the same secret bundle so they drift together.
- Run the whoami preflight in the deploy pipeline.
- Prefer user-id+password login for dedicated bot accounts - interactively minted tokens often belong to the operator.
When it happens
Trigger: Access-token login where whoami returns a user_id different from channels.matrix.user-id: the token was minted for another account (e.g. @ops:... while config declares @bot:...), or the user-id has a typo in the localpart or homeserver part.
Common situations: Copy-pasting a token from a different bot account; account deleted and re-created so the same localpart maps elsewhere; wrong homeserver suffix in the user-id; staging and production sharing tokens across accounts.
Related errors
- matrix: configured channels.matrix.device-id ({configured})
- matrix: whoami response did not include device_id; configure
- matrix: whoami request failed with HTTP {status}: {body}
- matrix: {reason} Cannot auto-recover because channels.matrix
- matrix login requires either access_token or user_id+passwor
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/11684395ef11e1a3.
Report an issue: GitHub.