**TL;DR**

An auth failure is not a transient error, so it should never be retried like one. Teach the send path to recognize the revoked-grant error specifically, and on the first sight of it: stop sending, mark the mailbox as needing re-authorization, and alert the operator. Every retry after a revoked grant is a guaranteed failure that still costs you quota and still looks bad in the logs. One detection, then silence until a human re-authorizes.

```text
agent's Gmail OAuth refresh token was revoked and it kept retrying sends for 6 hours, burning the daily quota on failures
```

## Steps

1. Match the revoked-grant error explicitly in your send path and classify it as auth_revoked, separate from rate limits, network errors, and bad recipients.
   Expected: a revoked grant in staging is classified as auth_revoked on the first failure, not retried as a transient error.
2. On auth_revoked, halt the send job immediately and mark the mailbox as needs_reauth. No retries, no waiting an hour to try again.
   Expected: the job halts after the first revoked-grant failure and the mailbox status flips to needs_reauth.
3. Alert the operator with the mailbox, the time, and the exact error, plus the re-authorization steps. The operator re-authorizes through the normal consent flow, then resumes the job.
   Expected: the alert names the mailbox and the job stays halted until the operator confirms re-authorization.
4. Keep transient-error retries and auth-error handling on completely separate paths. A retry policy that can't tell them apart will always burn quota on revoked grants.
   Expected: a rate-limit error retries with backoff while an auth error halts; a test of each shows the two different behaviors.
5. After re-authorization, resume from the last confirmed send, not from the start. The sends that failed during the outage were never delivered, so resume by checking what actually went out.
   Expected: resuming after re-auth sends only the unsent remainder, verified against the sent log.

## Use this when

- sends are failing with auth errors and the agent keeps retrying them
- an OAuth grant was revoked and the send job doesn't know it
- retry logic can't tell a revoked credential from a temporary failure

## Not for this skill when

- hitting the daily sending quota with valid credentials (that's a pacing problem)
- emails landing in spam despite successful sends (that's a deliverability problem)
- missing SPF or DKIM DNS records (that's a domain-authentication problem)

## Variant phrasings

- Gmail OAuth grant revoked, agent retried sends for hours burning quota
- refresh credential invalid, send loop kept failing instead of stopping
- auth error treated as transient, quota burned on guaranteed failures

## Why it happens

The OAuth grant was revoked, which made every send fail deterministically. But the retry logic only knew two categories, success and try-again, so it filed the auth failures under try-again and burned through the daily quota on sends that could never succeed. Auth failures are permanent until a human acts, and retrying them is pure waste.

## Edge cases

- a grant can also expire without being revoked; handle expiry with a proactive refresh before the send job starts, and revocation with the halt path
- the operator alert should say exactly which mailbox and which job, because the fix is per-mailbox re-authorization, not a global restart
- log the first auth failure loudly and suppress the rest; six hours of identical failures in the log helps nobody
- if multiple mailboxes share one app registration, one revoked user grant doesn't mean the others are dead; scope the halt to the affected mailbox

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_m5lbQgjM0VYbM4oVbQWWZQ
