Email Providers

Gmail SMTP Keeps Breaking After 7 Days: The invalid_grant Error Explained

There is a specific, maddening version of this problem: Gmail sending works perfectly, and then roughly a week later it stops. You reconnect, it works again, and a week later it stops again.

The error, if your plugin surfaces it, looks like this:

{ "error": "invalid_grant", "error_description": "Token has been expired or revoked." }

Nothing is wrong with your site. The cause is a single setting in the Google Cloud console, and it is the default.

The seven-day cause

When you create OAuth credentials in Google Cloud, the consent screen has a publishing status, and new projects start in Testing.

Refresh tokens issued by an app in Testing status expire after seven days. That is the entire mechanism. Your site holds a refresh token so it can keep sending without you re-authorising; on day seven Google invalidates it, the next send fails with invalid_grant, and mail stops.

It is a poor failure mode, because a week is long enough that the connection feels established. People reconnect, watch it work, and conclude the problem was transient.

The fix

  1. Open the Google Cloud console and select your project.
  2. Go to APIs & Services → OAuth consent screen.
  3. If publishing status is Testing, click Publish app to move it to In production.
  4. Reconnect your site to get a fresh token. The old one is already dead.

If the app only ever accesses your own Google Workspace accounts and uses restricted scopes, you may see a verification warning. For an internal-use application sending your own mail this does not block the change, and an app registered as Internal within a Workspace organisation skips it entirely.

The other causes of invalid_grant

Publishing status is the common one. If yours is already In production, work through these.

The Google account password was changed

Changing the password on the Google account that authorised the connection invalidates all its OAuth tokens immediately. Mail stops the moment somebody rotates a password, which makes the timing look unrelated to email.

This is a strong argument for authorising with a dedicated sending account rather than a person’s own — a departing employee’s password reset should not stop your order confirmations.

Six months of inactivity

A refresh token unused for six months is revoked. This affects low-traffic sites and staging environments that send rarely.

Too many tokens

There is a limit of 50 refresh tokens per user per OAuth client. Passing it starts silently invalidating the oldest. Repeatedly disconnecting and reconnecting while troubleshooting can quietly cause this.

The site was cloned

Tokens are bound to the site that authorised them. A staging copy of production holds a token it cannot use. This is worth understanding as a safety feature rather than a bug — it is one reason a clone does not immediately start mailing your customers — but it does mean staging needs its own connection or a sandbox.

The neighbouring errors

Two other Google OAuth errors are commonly confused with this one, and both are configuration rather than expiry.

redirect_uri_mismatch — the redirect URI your plugin uses is not listed in your OAuth client’s authorised URIs. It must match exactly, including protocol and any trailing slash. Copy it from the plugin rather than typing it.

unauthorized_client — most often caused by choosing the wrong application type when creating the credentials. You need Web application. Choosing Desktop app produces credentials that fail this way, and it is an easy mistake because “desktop” sounds right for a self-hosted site.

Publishing the app: what actually happens

People hesitate at “Publish app” because it sounds like listing something publicly. It does not.

Publishing changes the consent screen’s status from Testing to In production. Your application is not listed anywhere, not searchable, and not available to anyone who does not have your client credentials. What changes is that refresh tokens stop expiring after seven days.

You may see a warning about verification. Verification is required for applications requesting sensitive or restricted scopes that will be used by people outside your organisation. For a site sending its own mail, one of two things applies:

  • Google Workspace users can set the application’s user type to Internal, which limits it to your organisation and skips verification entirely. This is the right answer if you have Workspace.
  • Personal Gmail accounts cannot use Internal. You publish to production and may see an unverified-app screen during authorisation, which you can proceed past for your own account.

Neither path requires submitting anything to Google for review in order to send your own mail.

Sending limits you will eventually meet

Independent of tokens, Gmail applies limits designed for people rather than applications — commonly 500 messages a day on a free account and 2,000 on Workspace, with additional per-recipient counting.

The failure is unhelpful: sending stops, and the account may be temporarily restricted for up to 24 hours. On a store during a busy period this arrives as randomly missing order confirmations rather than a clear error.

There is also no delivery log, no bounce handling and no suppression list for application mail. When a customer says they never received a receipt, Gmail gives you nothing to check.

A quick decision

Your situation Reasonable choice
Brochure site, contact form, a few emails a day Gmail is fine — publish the app and move on
Workspace organisation, internal notifications Gmail with an Internal app type
Store sending order confirmations A transactional provider — no token expiry, no daily cap, real logs
Anything where a missed email costs money A transactional provider

Whether Gmail is the right choice here

Worth asking, because a lot of this friction is inherent.

Gmail and Google Workspace are mailbox services. Google applies sending limits designed for humans — commonly 500 messages a day on free Gmail and 2,000 on Workspace — and a busy store will meet them. There is also no delivery log, bounce handling or suppression management for application mail.

Gmail is a reasonable choice for a low-volume site, particularly a brochure site with a contact form. For a store sending order confirmations, a transactional provider will be less work overall — no OAuth app to register, no seven-day expiry, no daily cap, and actual delivery logs.

If you stay on Gmail, NimblyMail stores OAuth access and refresh tokens encrypted at rest rather than as plain text, and its scheduled audit will tell you that the connection has failed rather than leaving you to find out from a customer.

Quick reference

Symptom Cause Fix
Works for exactly 7 days, then invalid_grant Consent screen in Testing status Publish the app to production
Stopped abruptly, no pattern Google account password changed Reconnect; use a dedicated sending account
Stopped after months of little activity Token unused for 6 months Reconnect
Broke after repeated reconnects 50-token-per-client limit Reconnect; stop cycling connections
redirect_uri_mismatch URI not listed, or not an exact match Paste the plugin’s URI verbatim
unauthorized_client Created as Desktop app Recreate as Web application
Staging cannot send Token bound to the original site URL Separate connection, or use a sandbox
N
Ndeni
Keep reading

Related guides

Email Providers

Microsoft 365 Is Turning Off SMTP Basic Authentication: What WordPress Sites Must Do

At the end of December 2026, SMTP AUTH basic authentication is disabled by default for existing Microsoft 365 tenants. If your site sends mail…

Email Providers

Amazon SES for WordPress: The Region Trap That Breaks Most Setups

SES is cheap and reliable, and its most common failure has one cause: sandbox status, verified identities and production access are all per-region.

Email Providers

How to Send WordPress Email With Resend

Resend is the simplest transactional provider to set up, and the one most likely to be configured in a way that quietly fails DMARC.…

Leave a Reply

Your email address will not be published. Required fields are marked *