googleworkspace/cli · error · GwsError

Profile missing emailAddress

Error message

Profile missing emailAddress

What it means

The profile JSON parsed but `emailAddress` was absent or not a string — `profile.get("emailAddress").and_then(|v| v.as_str())` returned None. The Gmail profile endpoint guarantees `emailAddress` for a valid token with gmail scope, so this implies the endpoint returned something else: an empty object from a scope mismatch, an error envelope with 200, a stub response, or a future API change.

Source

Thrown at crates/google-workspace-cli/src/helpers/gmail/reply.rs:208

            .await
            .unwrap_or_else(|_| "(error body unreadable)".to_string());
        return Err(super::build_api_error(
            status,
            &body,
            "Failed to fetch user profile",
        ));
    }

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

    profile
        .get("emailAddress")
        .and_then(|v| v.as_str())
        .map(|s| s.to_string())
        .ok_or_else(|| GwsError::Other(anyhow::anyhow!("Profile missing emailAddress")))
}

// --- Message construction ---

fn extract_reply_to_address(original: &OriginalMessage) -> Vec<Mailbox> {
    match &original.reply_to {
        Some(reply_to) => reply_to.clone(),
        None => vec![original.from.clone()],
    }
}

fn build_reply_all_recipients(
    original: &OriginalMessage,
    extra_cc: Option<&[Mailbox]>,
    remove: Option<&[Mailbox]>,
    self_email: Option<&str>,
    from_alias: Option<&str>,
) -> Result<ReplyRecipients, GwsError> {

View on GitHub (pinned to a3768d0e82)

Solutions

  1. Verify scopes: run `gws auth status` and ensure gmail scope is granted, then `gws gmail users.getProfile --userId me` to inspect the live response.
  2. If a stub server is in play, add `emailAddress` to the fixture.
  3. Re-auth (`gws auth login`) if scopes are wrong or partial.
  4. Update the CLI if Gmail changed the profile shape.
Defensive patterns

Strategy: type-guard

Validate before calling

// Check scope coverage before reply-all runs: a token without gmail scope yields degenerate profiles
fn token_has_gmail_scope(scopes: &[String]) -> bool {
    scopes.iter().any(|s| s.contains("gmail"))
}

Type guard

fn profile_has_email(v: &serde_json::Value) -> bool {
    v.get("emailAddress").and_then(|e| e.as_str()).is_some_and(|s| !s.is_empty())
}

Try / catch

// Validate the envelope shape right after parse; fail with a scope-hint message:
let profile: Value = resp.json().await?;
if !profile_has_email(&profile) {
    return Err(anyhow::anyhow!("profile lacks emailAddress — token may be missing gmail scope; run gws auth login"));
}

Prevention

When it happens

Trigger: OAuth token granted for the wrong product (e.g. only People scope) causing a degenerate profile response; a mock server returning `{}`; API contract drift; a delegated/service account context where the profile resolves to an empty object.

Common situations: Scripts reusing a token minted for a different scope set; test fixtures that stub the profile endpoint minimally; rarely, Gmail API schema evolution.

Related errors


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