{"record":{"id":"5a226330e61498e4","repo":"Hmbown/CodeWhale","slug":"the-codewhale-service-returned-an-account-without","errorCode":null,"errorMessage":"The Codewhale service returned an account without an ID","messagePattern":"The Codewhale service returned an account without an ID","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":382,"sourceCode":"    }\n\n    fn save_auth(&self, bundle: AuthBundle) -> Result<()> {\n        self.account_store\n            .save(bundle)\n            .context(\"failed to save the Codewhale account session in the local secret store\")\n    }\n\n    fn clear_auth(&self) -> Result<()> {\n        self.account_store\n            .clear()\n            .context(\"failed to remove the local Codewhale account session\")\n    }\n\n    fn me(&self) -> Result<CloudUser> {\n        let response = self.execute_authenticated(HttpMethod::Get, \"/api/me\", None)?;\n        let me: MeResponse = expect_json(response, &[200])?;\n        if me.user.id.trim().is_empty() {\n            bail!(\"The Codewhale service returned an account without an ID\");\n        }\n        if let Some(mut stored) = self.load_auth()? {\n            stored.bundle.user = Some(me.user.clone());\n            self.save_auth(stored.bundle)?;\n        }\n        Ok(me.user)\n    }\n\n    fn set_key(&self, provider: CloudProvider, key: &str, label: &str) -> Result<()> {\n        let path = format!(\"/api/model-keys/{}\", provider.slug());\n        let response = self.execute_authenticated(\n            HttpMethod::Put,\n            &path,\n            Some(json_body(&ModelKeyRequest { key, label })?),\n        )?;\n        expect_empty(response, &[200, 201])\n    }\n","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/cloud.rs#L364-L400","documentation":"After a successful GET /api/me (HTTP 200, JSON parsed into MeResponse), the CLI validates that user.id is a non-empty trimmed string. An account record without an ID cannot be stored or addressed by later calls, so the response is rejected even though the HTTP call succeeded.","triggerScenarios":"The service returns 200 with a user object whose id field is missing (serde default), empty, or whitespace-only — e.g. malformed backend record, API contract change, or a mock/test server returning {\"user\": {}}.","commonSituations":"Developing against a stub /api/me that omits id, service regression that serializes anonymous/null accounts, or a version skew where the field was renamed and serde silently defaults it.","solutions":["Inspect the raw /api/me response (curl with the stored bearer token) and confirm the user object carries a non-empty id.","If you run a mock server, make it return a realistic id in the user object.","If the real service is returning id-less users, report it as a server-side data/regression issue with the response payload.","Update to matching CLI/service versions in case the field name changed."],"exampleFix":"# mock server before\n{\"user\": {\"email\": \"a@b.c\"}}\n\n# mock server after\n{\"user\": {\"id\": \"usr_123\", \"email\": \"a@b.c\"}}","handlingStrategy":"validation","validationCode":"// For mock/stub servers: guarantee the me payload shape\nfn valid_me_payload(user: &serde_json::Value) -> bool {\n    user.get(\"id\").and_then(|v| v.as_str()).map(|s| !s.trim().is_empty()).unwrap_or(false)\n}","typeGuard":"fn is_valid_cloud_user(v: &serde_json::Value) -> bool {\n    v.pointer(\"/user/id\")\n        .and_then(|id| id.as_str())\n        .map(|s| !s.trim().is_empty())\n        .unwrap_or(false)\n}","tryCatchPattern":"match client.me().await {\n    Ok(user) => user,\n    Err(e) if e.to_string().contains(\"without an ID\") => {\n        // 200-but-malformed: do not retry; capture raw response and report upstream\n        report_contract_violation(raw_response).await;\n        return Err(e);\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Make test doubles return realistic user objects with non-empty ids.","Pin CLI and service versions together in deployments.","Log raw /api/me bodies when contract errors appear."],"tags":["cloud","api-contract","validation","cli"],"backgroundTag":"api-schema-validation-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}