zeroclaw-labs/zeroclaw · error · anyhow::Error
matrix: whoami response did not include user_id
Error message
matrix: whoami response did not include user_id
What it means
The homeserver answered whoami with HTTP success, but the parsed JSON carried no usable user_id - the field was absent, null, or whitespace-only after trimming. Without a user_id the token's identity is unknown, so access-token login aborts. This points at a nonstandard or degraded homeserver response (or a URL that returns 200 for everything) rather than a ZeroClaw config error.
Source
Thrown at crates/zeroclaw-channels/src/matrix.rs:1687
.get(url)
.bearer_auth(access_token)
.send()
.await
.context("matrix: whoami request failed")?;
let status = response.status();
if !status.is_success() {
let body = read_whoami_error_body_preview(response).await;
bail!("matrix: whoami request failed with HTTP {status}: {body}");
}
let mut whoami = response
.json::<WhoamiResponse>()
.await
.context("matrix: failed to parse whoami response")?;
whoami.user_id = whoami.user_id.trim().to_string();
if whoami.user_id.is_empty() {
bail!("matrix: whoami response did not include user_id");
}
whoami.device_id = whoami
.device_id
.map(|device_id| device_id.trim().to_string())
.filter(|device_id| !device_id.is_empty());
Ok(whoami)
}
async fn read_whoami_error_body_preview(mut response: reqwest::Response) -> String {
let mut preview = Vec::new();
let mut truncated = false;
while preview.len() < WHOAMI_ERROR_BODY_PREVIEW_BYTES {
let chunk = match response.chunk().await {
Ok(Some(chunk)) => chunk,
Ok(None) => break,
Err(err) => return format!("failed to read response body: {err}"),View on GitHub (pinned to 88bb9c8533)
Solutions
- Inspect the raw response from the deployment host: curl -H 'Authorization: Bearer ...' https://HOST/_matrix/client/v3/account/whoami and confirm it returns a user_id field.
- If the URL returns something unexpected, fix channels.matrix.homeserver to the real client API base.
- If the server genuinely omits user_id, switch to user-id+password login (the whoami check belongs to the token path) or upgrade/fix the homeserver.
Defensive patterns
Strategy: try-catch
Validate before calling
async fn whoami_body_valid(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().is_some_and(|u| !u.trim().is_empty()))
} Try / catch
match start_matrix(config).await {
Err(err) if format!("{err:#}").contains("did not include user_id") => {
// not retryable: dump the homeserver's raw whoami body and compare
// against a known-good response; this is a server-shape problem
diagnostic_whoami_dump(&config).await;
return Err(err);
}
other => other,
} Prevention
- Run the whoami body preflight once at provisioning to catch nonstandard homeservers early.
- Prefer standard, maintained homeserver implementations for bot deployments.
- Keep password auth as a fallback for servers with quirky client API responses.
When it happens
Trigger: Whoami returns 200 with a body lacking user_id: a nonstandard or forked homeserver, an intercepting proxy returning an empty JSON object, or channels.matrix.homeserver pointing at a service that is not actually a Matrix client API.
Common situations: Custom homeserver implementations; middleware that rewrites responses; wrong DNS/URL landing on a generic web server that returns 200; API-version mismatches where the endpoint exists but responds oddly.
Related errors
- matrix: whoami response did not include device_id; configure
- matrix: whoami request failed with HTTP {status}: {body}
- matrix: configured channels.matrix.user-id ({configured}) do
- matrix: configured channels.matrix.device-id ({configured})
- matrix: {reason} Cannot auto-recover because channels.matrix
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/bea6cd62df1a32ee.
Report an issue: GitHub.