googleworkspace/cli · error · GwsError

Failed to parse sendAs response: {e}

Error message

Failed to parse sendAs response: {e}

What it means

`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.

Source

Thrown at crates/google-workspace-cli/src/helpers/gmail/mod.rs:479

    .map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to fetch sendAs settings: {e}")))?;

    if !resp.status().is_success() {
        let status = resp.status().as_u16();
        let body = resp
            .text()
            .await
            .unwrap_or_else(|_| "(error body unreadable)".to_string());
        return Err(build_api_error(
            status,
            &body,
            "Failed to fetch sendAs settings",
        ));
    }

    let body: Value = resp
        .json()
        .await
        .map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to parse sendAs response: {e}")))?;

    Ok(parse_send_as_response(&body))
}

/// Parse the JSON response from the sendAs.list endpoint into identities.
fn parse_send_as_response(body: &Value) -> Vec<SendAsIdentity> {
    let empty = vec![];
    let entries = body
        .get("sendAs")
        .and_then(|v| v.as_array())
        .unwrap_or(&empty);

    entries
        .iter()
        .filter_map(|entry| {
            let email = entry.get("sendAsEmail")?.as_str()?;
            let display_name = entry
                .get("displayName")

View on GitHub (pinned to a3768d0e82)

Solutions

  1. Retry once — truncation is usually transient.
  2. Bypass any intercepting proxy for gmail.googleapis.com and re-run.
  3. If using a stub server in tests, make it return `{"sendAs": []}` with `Content-Type: application/json`.
  4. Capture the raw body (see example fix) to identify the interceptor.

Example fix

// before
let body: Value = resp.json().await.map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to parse sendAs response: {e}")))?;

// after: require JSON content-type up front so interceptors fail loudly at the right layer
if !resp.headers().get(reqwest::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()).is_some_and(|ct| ct.starts_with("application/json")) {
    let snippet = resp.text().await.unwrap_or_default();
    return Err(GwsError::Other(anyhow::anyhow!("sendAs endpoint returned non-JSON body: {}", &snippet[..snippet.len().min(120)])));
}
let body: Value = resp.json().await.map_err(|e| GwsError::Other(anyhow::anyhow!("Failed to parse sendAs response: {e}")))?;
Defensive patterns

Strategy: validation

Validate before calling

// Require JSON content type and non-empty body before json()
let ct = resp.headers().get(reqwest::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()).unwrap_or("");
if !ct.contains("json") { return Err(anyhow::anyhow!("non-JSON sendAs response: {ct}")); }

Type guard

fn is_send_as_envelope(v: &serde_json::Value) -> bool {
    // sendAs key is optional (empty account) but the object must not carry error/html markers
    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)
}

Try / catch

let text = resp.text().await?;
let body: Value = serde_json::from_str(&text)
    .map_err(|e| anyhow::anyhow!("sendAs body not JSON ({e}): {}", &text[..text.len().min(120)]))?;

Prevention

When it happens

Trigger: 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.

Common situations: Corporate TLS inspection rewriting googleapis responses; scripting `+send` on unstable networks; local dev pointing at a fake API server.

Understand the failure class

Related errors


AI-assisted analysis of googleworkspace/cli@a3768d0e82 (2026-08-16). Data as JSON: /api/errors/8aadebd643f87064. Report an issue: GitHub.