Skip to content

gmail +read returns 403 quota-project error when authenticated with default scopes #843

@sheurich

Description

@sheurich

gws gmail +read fails with:

Your application is authenticating by using local Application Default Credentials.
The gmail.googleapis.com API requires a quota project, which is not set by default.

The equivalent raw command succeeds:

gws gmail users messages get --params '{"userId":"me","id":"<id>","format":"full"}'

Cause

Google identifies tokens from the Cloud SDK client (764086051850-...) as ADC and requires the x-goog-user-project header. Standard resource commands go through executor.rs, which calls auth::get_quota_project() and adds the header. The +read helper bypasses executor.rs and calls fetch_message_metadata directly via crate::client::build_client() and send_with_retry. The request goes out with .bearer_auth(token) only, no quota project header.

This gap exists because #242 moved the x-goog-user-project injection from build_client() into executor.rs. When +read was added in #526, it called build_client() directly and missed the header.

Reproduction

gws auth login   # default scopes (gmail.modify)
gws gmail +read --id <any-message-id>
# -> 403 accessNotConfigured
gws gmail users messages get --params '{"userId":"me","id":"<id>","format":"full"}'
# -> succeeds

Adding gmail.readonly to the token does not help — the scope is irrelevant. The missing header is the sole cause.

Related

Suggested fix

Add x-goog-user-project via auth::get_quota_project() in fetch_message_metadata (and other helper functions that bypass executor.rs). Same trade-off as #729 applies.

Alternatively, refactor helpers to route through executor.rs or a shared request-building function that handles auth headers consistently.

Workaround

gws gmail users messages get --params '{"userId":"me","id":"<message-id>","format":"full"}'

Environment

  • gws 0.22.5, macOS (Apple Silicon)
  • Auth: OAuth desktop, keyring backend
  • Confirmed with both gmail.modify only and gmail.modify + gmail.readonly
  • ADC quota_project_id set to a project with Gmail API enabled (irrelevant — build_client() does not propagate it)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions