{"record":{"id":"11684395ef11e1a3","repo":"zeroclaw-labs/zeroclaw","slug":"matrix-configured-channels-matrix-user-id-confi","errorCode":null,"errorMessage":"matrix: configured channels.matrix.user-id ({configured}) does not match Matrix whoami user_id ({})","messagePattern":"matrix: configured channels\\.matrix\\.user-id \\((.+?)\\) does not match Matrix whoami user_id \\((.+?)\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/matrix.rs","lineNumber":1633,"sourceCode":"    ) -> Result<AccessTokenIdentity> {\n        let configured_user_id = non_empty_config_value(config.user_id.as_deref());\n        let configured_device_id = non_empty_config_value(config.device_id.as_deref());\n\n        if let (Some(user_id), Some(device_id)) =\n            (configured_user_id.as_ref(), configured_device_id.as_ref())\n        {\n            return Ok(AccessTokenIdentity {\n                user_id: user_id.clone(),\n                device_id: Some(device_id.clone()),\n            });\n        }\n\n        let whoami = fetch_access_token_whoami(config).await?;\n\n        if let Some(ref configured) = configured_user_id\n            && configured != &whoami.user_id\n        {\n            bail!(\n                \"matrix: configured channels.matrix.user-id ({configured}) does not match Matrix whoami user_id ({})\",\n                whoami.user_id\n            );\n        }\n\n        if let (Some(configured), Some(actual)) = (&configured_device_id, &whoami.device_id)\n            && configured != actual\n        {\n            bail!(\n                \"matrix: configured channels.matrix.device-id ({configured}) does not match Matrix whoami device_id ({actual})\"\n            );\n        }\n\n        if configured_device_id.is_none() && whoami.device_id.is_none() {\n            bail!(\n                \"matrix: whoami response did not include device_id; configure channels.matrix.device-id for access-token login\"\n            );\n        }","sourceCodeStart":1615,"sourceCodeEnd":1651,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/matrix.rs#L1615-L1651","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: token actually belongs to @ops:example.org\n[channels.matrix]\nuser-id = \"@bot:example.org\"\naccess-token = \"syt_...\"\n\n# after\n[channels.matrix]\nuser-id = \"@ops:example.org\"\naccess-token = \"syt_...\"","handlingStrategy":"validation","validationCode":"async fn token_identity_matches(cfg: &MatrixConfig) -> anyhow::Result<bool> {\n    let url = format!(\n        \"{}/_matrix/client/v3/account/whoami\",\n        cfg.homeserver.trim_end_matches('/')\n    );\n    let who: serde_json::Value = reqwest::Client::new()\n        .get(url)\n        .bearer_auth(cfg.access_token.as_deref().unwrap_or_default())\n        .send().await?\n        .error_for_status()?\n        .json().await?;\n    Ok(who[\"user_id\"].as_str() == cfg.user_id.as_deref())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["matrix","auth","access-token","whoami","identity","config"],"backgroundTag":"auth-identity-mismatch","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}