{"record":{"id":"6a69ed7f83ae9ccb","repo":"googleworkspace/cli","slug":"failed-to-parse-people-api-response-e","errorCode":null,"errorMessage":"Failed to parse People API response: {e}","messagePattern":"Failed to parse People API response: (.+?)","errorType":"exception","errorClass":"GwsError","httpStatus":null,"severity":"error","filePath":"crates/google-workspace-cli/src/helpers/gmail/mod.rs","lineNumber":667,"sourceCode":"        client\n            .get(\"https://people.googleapis.com/v1/people/me\")\n            .query(&[(\"personFields\", \"names\")])\n            .bearer_auth(token)\n    })\n    .await\n    .map_err(|e| GwsError::Other(anyhow::anyhow!(\"People API request failed: {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(build_api_error(status, &body, \"People API request failed\"));\n    }\n\n    let body: Value = resp.json().await.map_err(|e| {\n        GwsError::Other(anyhow::anyhow!(\"Failed to parse People API response: {e}\"))\n    })?;\n\n    Ok(parse_profile_display_name(&body))\n}\n\n/// Extract the display name from a People API `people.get` response.\nfn parse_profile_display_name(body: &Value) -> Option<String> {\n    body.get(\"names\")\n        .and_then(|v| v.as_array())\n        .and_then(|names| names.first())\n        .and_then(|n| n.get(\"displayName\"))\n        .and_then(|v| v.as_str())\n        .filter(|s| !s.is_empty())\n        .map(sanitize_control_chars)\n}\n\n/// Fetch binary data for a single attachment from the Gmail API.\n///","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/googleworkspace/cli/blob/a3768d0e82ad83cca2da97724e46bea4ff0e6dbd/crates/google-workspace-cli/src/helpers/gmail/mod.rs#L649-L685","documentation":"The People API responded 2xx but `resp.json::<Value>()` could not parse the body as JSON. The People API normally returns well-formed JSON, so a malformed body almost always means an intermediary (proxy, captive portal) rewrote the response, or the body was truncated in transit.","triggerScenarios":"Intercepting proxy returning an HTML block page with 200; connection cut mid-body so every retry also truncates; stub server in tests returning non-JSON.","commonSituations":"Same contexts as error 27 — corporate proxies and unstable networks — plus test harnesses that stub people.googleapis.com incorrectly.","solutions":["Retry the gmail helper command.","Confirm no proxy rewrites people.googleapis.com responses.","In tests, stub the endpoint with `{\"names\": []}` and a JSON content type.","Log the raw body prefix to identify the interceptor (see example fix pattern)."],"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.starts_with(\"application/json\") {\n    return Err(anyhow::anyhow!(\"People API returned '{ct}' — likely proxy interception\"));\n}","typeGuard":"fn is_people_get_response(v: &serde_json::Value) -> bool {\n    // resourceName is mandatory on people.get responses; names may be absent\n    v.get(\"resourceName\").and_then(|r| r.as_str()).is_some()\n}","tryCatchPattern":"let text = resp.text().await?;\nlet body: Value = serde_json::from_str(&text)\n    .map_err(|e| anyhow::anyhow!(\"People API invalid JSON ({e}): {}\", &text[..text.len().min(120)]))?;\nif !is_people_get_response(&body) { return Err(anyhow::anyhow!(\"unexpected People API envelope\")); }","preventionTips":["Validate content type before json() on People API calls.","Stub people.googleapis.com with `{\"resourceName\": \"people/me\", \"names\": []}` in tests.","Retry once before escalating — truncation is transient."],"tags":["people-api","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"}