{"record":{"id":"7366e2c78a2131aa","repo":"googleworkspace/cli","slug":"failed-to-parse-profile-e","errorCode":null,"errorMessage":"Failed to parse profile: {e}","messagePattern":"Failed to parse profile: (.+?)","errorType":"exception","errorClass":"GwsError","httpStatus":null,"severity":"error","filePath":"crates/google-workspace-cli/src/helpers/gmail/reply.rs","lineNumber":202,"sourceCode":"    .map_err(|e| GwsError::Other(anyhow::anyhow!(\"Failed to fetch user profile: {e}\")))?;\n\n    if !resp.status().is_success() {\n        let status = resp.status().as_u16();\n        let body = resp\n            .text()\n            .await\n            .unwrap_or_else(|_| \"(error body unreadable)\".to_string());\n        return Err(super::build_api_error(\n            status,\n            &body,\n            \"Failed to fetch user profile\",\n        ));\n    }\n\n    let profile: Value = resp\n        .json()\n        .await\n        .map_err(|e| GwsError::Other(anyhow::anyhow!(\"Failed to parse profile: {e}\")))?;\n\n    profile\n        .get(\"emailAddress\")\n        .and_then(|v| v.as_str())\n        .map(|s| s.to_string())\n        .ok_or_else(|| GwsError::Other(anyhow::anyhow!(\"Profile missing emailAddress\")))\n}\n\n// --- Message construction ---\n\nfn extract_reply_to_address(original: &OriginalMessage) -> Vec<Mailbox> {\n    match &original.reply_to {\n        Some(reply_to) => reply_to.clone(),\n        None => vec![original.from.clone()],\n    }\n}\n\nfn build_reply_all_recipients(","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/googleworkspace/cli/blob/a3768d0e82ad83cca2da97724e46bea4ff0e6dbd/crates/google-workspace-cli/src/helpers/gmail/reply.rs#L184-L220","documentation":"The Gmail profile endpoint returned 2xx but `resp.json::<Value>()` failed to parse the body. The profile endpoint always returns JSON (`{\"emailAddress\": ..., \"messagesTotal\": ...}`), so a non-JSON body indicates an intercepting proxy, captive portal, or truncated response rather than anything scope- or token-related.","triggerScenarios":"Proxy returning 200 with HTML; body truncated mid-transfer on unstable links; stub server misconfigured in tests.","commonSituations":"reply-all runs on corporate networks with TLS inspection; CI environments routing googleapis traffic through a rewriting gateway.","solutions":["Retry the reply-all command.","Confirm no proxy rewrites gmail.googleapis.com responses (curl the profile endpoint and inspect).","For test stubs, return `{\"emailAddress\": \"user@example.com\"}` with JSON content type.","Capture body prefix in logs if it persists."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let ct = resp.headers().get(reqwest::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()).unwrap_or(\"\");\nif !ct.contains(\"json\") { return Err(anyhow::anyhow!(\"profile response not JSON ({ct})\")); }","typeGuard":"fn is_profile_envelope(v: &serde_json::Value) -> bool {\n    v.get(\"emailAddress\").and_then(|e| e.as_str()).is_some() || v.get(\"historyId\").is_some()\n}","tryCatchPattern":"let text = resp.text().await?;\nlet profile: Value = serde_json::from_str(&text)\n    .map_err(|e| anyhow::anyhow!(\"profile body not JSON ({e}): {}\", &text[..text.len().min(120)]))?;\nif !is_profile_envelope(&profile) { return Err(anyhow::anyhow!(\"unexpected profile envelope\")); }","preventionTips":["Content-type check before json() catches interceptor pages masquerading as 200.","Retry once on parse failure.","Stub the profile endpoint as `{\"emailAddress\": \"user@example.com\"}` in tests."],"tags":["gmail","profile","json","response-parsing","proxy"],"backgroundTag":"invalid-json-response","analyzedSha":"a3768d0e82ad83cca2da97724e46bea4ff0e6dbd","analyzedAt":"2026-08-16T19:51:46.516Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}