{"record":{"id":"bea6cd62df1a32ee","repo":"zeroclaw-labs/zeroclaw","slug":"matrix-whoami-response-did-not-include-user-id","errorCode":null,"errorMessage":"matrix: whoami response did not include user_id","messagePattern":"matrix: whoami response did not include user_id","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/matrix.rs","lineNumber":1687,"sourceCode":"            .get(url)\n            .bearer_auth(access_token)\n            .send()\n            .await\n            .context(\"matrix: whoami request failed\")?;\n        let status = response.status();\n\n        if !status.is_success() {\n            let body = read_whoami_error_body_preview(response).await;\n            bail!(\"matrix: whoami request failed with HTTP {status}: {body}\");\n        }\n\n        let mut whoami = response\n            .json::<WhoamiResponse>()\n            .await\n            .context(\"matrix: failed to parse whoami response\")?;\n        whoami.user_id = whoami.user_id.trim().to_string();\n        if whoami.user_id.is_empty() {\n            bail!(\"matrix: whoami response did not include user_id\");\n        }\n        whoami.device_id = whoami\n            .device_id\n            .map(|device_id| device_id.trim().to_string())\n            .filter(|device_id| !device_id.is_empty());\n\n        Ok(whoami)\n    }\n\n    async fn read_whoami_error_body_preview(mut response: reqwest::Response) -> String {\n        let mut preview = Vec::new();\n        let mut truncated = false;\n\n        while preview.len() < WHOAMI_ERROR_BODY_PREVIEW_BYTES {\n            let chunk = match response.chunk().await {\n                Ok(Some(chunk)) => chunk,\n                Ok(None) => break,\n                Err(err) => return format!(\"failed to read response body: {err}\"),","sourceCodeStart":1669,"sourceCodeEnd":1705,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/matrix.rs#L1669-L1705","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"async fn whoami_body_valid(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().is_some_and(|u| !u.trim().is_empty()))\n}","typeGuard":null,"tryCatchPattern":"match start_matrix(config).await {\n    Err(err) if format!(\"{err:#}\").contains(\"did not include user_id\") => {\n        // not retryable: dump the homeserver's raw whoami body and compare\n        // against a known-good response; this is a server-shape problem\n        diagnostic_whoami_dump(&config).await;\n        return Err(err);\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["matrix","whoami","auth","parse","homeserver"],"backgroundTag":"incomplete-whoami-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}