{"record":{"id":"8aadebd643f87064","repo":"googleworkspace/cli","slug":"failed-to-parse-sendas-response-e","errorCode":null,"errorMessage":"Failed to parse sendAs response: {e}","messagePattern":"Failed to parse sendAs response: (.+?)","errorType":"exception","errorClass":"GwsError","httpStatus":null,"severity":"error","filePath":"crates/google-workspace-cli/src/helpers/gmail/mod.rs","lineNumber":479,"sourceCode":"    .map_err(|e| GwsError::Other(anyhow::anyhow!(\"Failed to fetch sendAs settings: {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(\n            status,\n            &body,\n            \"Failed to fetch sendAs settings\",\n        ));\n    }\n\n    let body: Value = resp\n        .json()\n        .await\n        .map_err(|e| GwsError::Other(anyhow::anyhow!(\"Failed to parse sendAs response: {e}\")))?;\n\n    Ok(parse_send_as_response(&body))\n}\n\n/// Parse the JSON response from the sendAs.list endpoint into identities.\nfn parse_send_as_response(body: &Value) -> Vec<SendAsIdentity> {\n    let empty = vec![];\n    let entries = body\n        .get(\"sendAs\")\n        .and_then(|v| v.as_array())\n        .unwrap_or(&empty);\n\n    entries\n        .iter()\n        .filter_map(|entry| {\n            let email = entry.get(\"sendAsEmail\")?.as_str()?;\n            let display_name = entry\n                .get(\"displayName\")","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/googleworkspace/cli/blob/a3768d0e82ad83cca2da97724e46bea4ff0e6dbd/crates/google-workspace-cli/src/helpers/gmail/mod.rs#L461-L497","documentation":"`fetch_send_as_identities` got a 2xx from the sendAs list endpoint but `resp.json::<Value>()` failed — the body was not parseable JSON. Expected shape is `{\"sendAs\": [ ... ]}`; anything else (HTML from an interceptor, empty body, truncated stream) lands here. Downstream `parse_send_as_response` is lenient (missing `sendAs` key yields an empty vec), so this error only fires on malformed JSON itself.","triggerScenarios":"Proxy/captive portal returning 200 with HTML; truncated response body on a dying connection; a mock/stub test server returning non-JSON; charset mislabeling by an intermediary.","commonSituations":"Corporate TLS inspection rewriting googleapis responses; scripting `+send` on unstable networks; local dev pointing at a fake API server.","solutions":["Retry once — truncation is usually transient.","Bypass any intercepting proxy for gmail.googleapis.com and re-run.","If using a stub server in tests, make it return `{\"sendAs\": []}` with `Content-Type: application/json`.","Capture the raw body (see example fix) to identify the interceptor."],"exampleFix":"// before\nlet body: Value = resp.json().await.map_err(|e| GwsError::Other(anyhow::anyhow!(\"Failed to parse sendAs response: {e}\")))?;\n\n// after: require JSON content-type up front so interceptors fail loudly at the right layer\nif !resp.headers().get(reqwest::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()).is_some_and(|ct| ct.starts_with(\"application/json\")) {\n    let snippet = resp.text().await.unwrap_or_default();\n    return Err(GwsError::Other(anyhow::anyhow!(\"sendAs endpoint returned non-JSON body: {}\", &snippet[..snippet.len().min(120)])));\n}\nlet body: Value = resp.json().await.map_err(|e| GwsError::Other(anyhow::anyhow!(\"Failed to parse sendAs response: {e}\")))?;","handlingStrategy":"validation","validationCode":"// Require JSON content type and non-empty body before json()\nlet 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!(\"non-JSON sendAs response: {ct}\")); }","typeGuard":"fn is_send_as_envelope(v: &serde_json::Value) -> bool {\n    // sendAs key is optional (empty account) but the object must not carry error/html markers\n    v.get(\"sendAs\").map(|s| s.is_array() || s.is_null()).unwrap_or(false) || v.as_object().map(|o| o.is_empty()).unwrap_or(false)\n}","tryCatchPattern":"let text = resp.text().await?;\nlet body: Value = serde_json::from_str(&text)\n    .map_err(|e| anyhow::anyhow!(\"sendAs body not JSON ({e}): {}\", &text[..text.len().min(120)]))?;","preventionTips":["Validate Content-Type before parsing googleapis responses in your own code.","Stub sendAs endpoints in tests with `{\"sendAs\": []}`.","Retry on parse failure; blame the proxy if it repeats."],"tags":["gmail","sendas","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"}