Source: https://cli.nylas.com/guides/gmail-api-error-codes

# Gmail API Error Codes and How to Fix Them

The Gmail API returns HTTP status codes that look generic but mean specific things — a 403 is usually a rate limit, not a permission problem, and a 412 means your sync token is stale. Reading them right is the difference between a one-line fix and an hour of guessing. This guide is a reference for the Gmail API errors developers hit most, what each actually means, the correct fix, and how the Nylas CLI removes a whole class of them by handling auth and sync for you.

Written by [Caleb Geene](https://cli.nylas.com/authors/caleb-geene) Director, Site Reliability Engineering

Updated June 8, 2026

> **TL;DR:** On the Gmail API, `401` means the access token expired (refresh it), `403` is almost always a rate-limit or quota error (back off), `412` means a stale `historyId` (do a full resync), `429` is explicit throttling (honor `Retry-After`), and `5xx` is transient (retry with backoff). The Nylas CLI handles token refresh and sync, so the auth and history errors mostly disappear.

Command references used in this guide: [`nylas email list`](https://cli.nylas.com/docs/commands/email-list), [`nylas auth status`](https://cli.nylas.com/docs/commands/auth-status), and [`nylas doctor`](https://cli.nylas.com/docs/commands/doctor).

## What do the common Gmail API error codes mean?

The Gmail API reuses standard HTTP status codes, but each maps to a specific Gmail cause that the bare number hides. The table lists the five developers hit most, with the real meaning and the fix. Google's full list is in the [Gmail API error-handling guide](https://developers.google.com/gmail/api/guides/handle-errors); the rows below are the ones that account for most real-world failures.

| Code | Real meaning | Fix |
| --- | --- | --- |
| 401 | Access token expired/invalid | Refresh the token; reconnect if revoked |
| 403 | Rate limit / quota exceeded | Back off; check the reason field |
| 412 | Stale historyId (sync token) | Do a full sync, then resume deltas |
| 429 | Too many requests (throttled) | Honor Retry-After; exponential backoff |
| 500 / 503 | Transient server error | Retry with jittered backoff |

## Why is a Gmail 403 usually not a permissions error?

A Gmail `403` trips people up because the code normally means “forbidden,” but on the Gmail API it is most often a rate-limit or quota error, not a missing scope. The body's `reason` field disambiguates: `rateLimitExceeded` and `userRateLimitExceeded` are throttling, while `insufficientPermissions` is the actual scope problem. Read the reason before assuming your OAuth scopes are wrong.

The default quota is large — 1,000,000,000 quota units per day per project — but the per-user limit of 250 units per second is what bursty code hits first. The fix for the throttling cases is exponential backoff with jitter, not a scope change. Reserve a scope fix for an actual `insufficientPermissions` reason, which means reconnecting with the right access.

```json
# A Gmail 403 body tells you which 403 it is:
# { "error": { "code": 403, "errors": [
#     { "reason": "rateLimitExceeded" }        <- back off, don't touch scopes
#     { "reason": "insufficientPermissions" }  <- reconnect with the right scope
# ] } }
```

## How does the CLI remove a class of these errors?

The CLI removes the auth and sync errors by handling them for you. Token refresh is automatic, so the `401`-after-an-hour failure that plagues hand-rolled integrations doesn't happen; the grant stays current behind `nylas email list`. And because the CLI manages incremental sync, you never hold a raw `historyId` to go stale and return a `412`.

When something genuinely is wrong — a revoked grant, a real permission gap — `nylas auth status` and `nylas doctor` report it in plain terms instead of a bare status code. That turns “why am I getting a 401?” into a one-command diagnosis. The throttling and transient errors still apply at high volume, which the rate-limit guide covers in detail.

```bash
# Diagnose auth/sync state in plain language instead of decoding status codes
nylas auth status
nylas doctor --json | jq '.checks'

# Token refresh and sync are handled, so this just works hours later:
nylas email list --json --limit 10
```

## Next steps

- [Fix 429 rate-limit errors](https://cli.nylas.com/guides/email-api-429-rate-limit-fix) — backoff and Retry-After across providers
- [Microsoft Graph error codes](https://cli.nylas.com/guides/graph-api-error-codes) — the Outlook equivalent
- [Gmail API quotas](https://cli.nylas.com/guides/gmail-api-quotas-2026) — the unit budget behind 403/429
- [Fix the invalid_grant error](https://cli.nylas.com/guides/fix-invalid-grant-error) — when a refresh token is dead
- [Debug email delivery](https://cli.nylas.com/guides/debug-email-delivery-cli) — when a sent message never arrives
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
