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

# Microsoft Graph Error Codes and Fixes

Microsoft Graph returns HTTP status codes plus a more specific error code in the body, and the body is where the real answer lives — a 403 might be a missing consent or a conditional-access block, and a 429 always carries a Retry-After you must honor. This guide is a reference for the Graph mail and calendar errors developers hit most, what each means, the correct fix, and how the Nylas CLI removes the auth and throttling-handling burden by managing tokens and retries.

Written by [Prem Keshari](https://cli.nylas.com/authors/prem-keshari) Senior SRE

Updated June 8, 2026

> **TL;DR:** On Microsoft Graph, `401` means the token expired or is invalid (refresh it), `403` is a consent or conditional-access problem (not throttling), `404` often means a missing resource *or* hidden permission, `429` is throttling with a mandatory `Retry-After` header, and `503` is a transient service issue. The Nylas CLI manages tokens and retries, so the auth and throttling code mostly goes away.

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 Graph error codes mean?

Microsoft Graph returns an HTTP status plus an `error.code` string in the body, and the string is what you act on. The table lists the five mail-and-calendar errors developers hit most, with the real cause and the fix. Microsoft documents the full set in the [Microsoft Graph error reference](https://learn.microsoft.com/en-us/graph/errors); these rows are the ones behind most production failures.

| Code | Real meaning | Fix |
| --- | --- | --- |
| 401 | Token expired/invalid (InvalidAuthenticationToken) | Refresh; reconnect if revoked |
| 403 | Missing consent / conditional access | Grant the scope; check CA policy |
| 404 | Resource missing or permission hidden | Verify the ID and the scope |
| 429 | Throttled (TooManyRequests) | Honor Retry-After; back off |
| 503 | Service unavailable (transient) | Retry with jittered backoff |

## How do you handle Graph 429 throttling?

Handle a Graph `429` by reading the `Retry-After` header and waiting exactly that many seconds before retrying — Microsoft requires honoring it, and ignoring it extends the throttling window. Graph throttles per app and per mailbox, and limits vary by workload, so the only correct strategy is to obey the header rather than guess a delay. Treat `429` as “wait, then retry,” never “retry immediately.”

Beyond honoring `Retry-After`, reduce the request volume that triggers throttling: batch reads, request only the fields you need with `$select`, and use delta queries instead of repeated full reads. A client that respects the header and trims its calls rarely sees sustained throttling. The same backoff approach covers the transient `503`, which signals a temporary service issue.

```text
# A Graph 429 carries the wait time — obey it:
# HTTP/1.1 429 Too Many Requests
# Retry-After: 12
#
# Pseudocode:
#   if status == 429: sleep(response.headers["Retry-After"]); retry()
#   else if status >= 500: sleep(backoff_with_jitter()); retry()
```

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

The CLI removes the auth burden by refreshing tokens for you, so the `401` InvalidAuthenticationToken that hits hand-rolled Graph clients after an hour doesn't recur — the grant stays current behind `nylas email list`. It also skips the Azure app registration that causes much of the `403` consent pain, because the OAuth app is configured once in the Nylas dashboard and each user consents through one flow.

When a real problem exists — a revoked grant, a conditional-access block — `nylas auth status` and `nylas doctor` name it instead of returning a raw status code. That converts Graph's sometimes-cryptic errors into a plain diagnosis. At high volume you still mind throttling, which the rate-limit guide covers across providers.

```bash
# Plain-language diagnosis instead of decoding Graph error bodies
nylas auth status
nylas doctor --json | jq '.checks'

# Token refresh handled — no 401-after-an-hour on a long-running job
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
- [Gmail API error codes](https://cli.nylas.com/guides/gmail-api-error-codes) — the Google equivalent
- [Microsoft Graph vs Nylas](https://cli.nylas.com/guides/microsoft-graph-vs-nylas) — why the CLI skips Azure setup
- [Fix the invalid_grant error](https://cli.nylas.com/guides/fix-invalid-grant-error) — when a refresh token is dead
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
