Any platform · Updated · 9 min read · Lukas Ceponis
OAuth stopped working after weeks: token expiry, traced and fixed
Credential monitor (n8n workflow)A scheduled workflow that pings every connected service once a day and messages you while the token is still alive.OAuth stopped working after weeks of clean runs. Nobody edited the workflow, nobody rotated a key, and this morning the Google node returns a 401 on a connection that has been fine since spring. This is the single most common thing we get called about, and the cause is almost never inside your automation logic: a token issued to somebody's account reached the end of its life or was revoked somewhere else entirely, and most platforms have no way to tell you that before the next run walks into it.
Below are the four shapes this failure takes with the log output for each, a table of how long tokens actually live at the providers people connect most, and a scheduled workflow you can import today so the next expiry arrives as a message to you rather than a complaint from a customer.
OAuth stopped working after weeks: four shapes of the same failure
The refresh call comes back with invalid_grant
Every OAuth integration holds two credentials. There is a short-lived access token it attaches to each request, and a refresh token it uses quietly in the background to get a fresh access token when the old one dies. When the refresh token itself is dead, the failure surfaces at the token endpoint, which is why the error text talks about a grant rather than about your account.
POST https://oauth2.googleapis.com/token
HTTP/1.1 400 Bad Request
{
"error": "invalid_grant",
"error_description": "Token has been expired or revoked."
}
Your automation platform wraps that in something friendlier and considerably less useful. In n8n the same event reaches the execution log looking like this, which is why "google credentials expired n8n" is such a well-worn search:
NodeApiError: Authorization failed - please check your credentials node: "Google Sheets" httpCode: "401" description: "Forbidden - perhaps check your credentials?"
The suggestion misleads in a specific way. The credential record inside n8n is intact, the account email still displays next to it, and the connection looks connected. What died is at Google's end, so reopening the credential and clicking through the consent screen again is the entire fix, and no amount of reading the workflow will show you anything.
403 with an insufficient scope body
This one usually appears after a reconnection, when somebody granted fewer permissions the second time around. Authentication passes and the operation fails.
GET https://www.googleapis.com/drive/v3/files?q=...
HTTP/1.1 403 Forbidden
{
"error": {
"code": 403,
"message": "Request had insufficient authentication scopes.",
"status": "PERMISSION_DENIED",
"errors": [
{ "domain": "global",
"reason": "insufficientPermissions",
"message": "Insufficient Permission" }
]
}
}
The reason field is the part worth reading. insufficientPermissions says the token is perfectly valid and simply does not carry the scope this particular call needs. It happens when someone unticks a checkbox on the Google consent screen in a hurry, and it also happens when the app's requested scope list changed between the original connection and today. Reconnecting again without fixing the scope produces the identical 403, which is how people end up reconnecting the same credential five times in an afternoon and concluding the platform is broken.
Somebody revoked access and the automation never heard
Revocation is not always an act of sabotage. A password change, an employee offboarding, an admin cleaning up third-party apps, or a security policy firing on a risk signal will all do it. The failure shape here is nastier because the HTTP layer often reports success.
POST https://slack.com/api/chat.postMessage
HTTP/1.1 200 OK
{ "ok": false, "error": "token_revoked" }
Look at the status line. The request succeeded, the response body carries the bad news, and a node that only checks the status code will pass this along with a green tick while delivering nothing to anyone. That is one of the paths into a workflow that runs and nothing happens. On the Microsoft side the same class of event is legible, at least:
AADSTS50173: The provided grant has expired due to it being revoked, a fresh auth token is needed. The user might have changed or reset their password.
One account out of several dies and the run still reports success
The expensive version of this problem is the partial one. A workflow loops over four mailboxes or six ad accounts, one credential dies, the loop is set to continue on failure, and the execution list stays green for weeks.
[08:14:02] Send digest ok mailbox=ops@ [08:14:03] Send digest ok mailbox=sales@ [08:14:04] Send digest 401 mailbox=founder@ invalid_grant [08:14:05] Send digest ok mailbox=support@ [08:14:07] Execution finished status=success items=3/4
Nobody reads 3/4 as a failure at a glance. In one rescue we scoped, a lead-routing flow running about 200 times a day was failing roughly once a day on exactly this pattern, and the only alert it produced went to a Slack channel that had been archived months earlier. The leads were gone before anybody knew there was anything to look for. If your automation stopped working and credentials are the suspect, the first question worth asking is how many recent runs finished green while delivering nothing.
Token lifetimes by provider
These are the behaviours we rely on when we build monitoring. Where a value is set by the app owner or an admin policy rather than fixed by the provider, the table says so instead of pretending to a number.
| Provider | Access token | Refresh or long-lived credential | What kills it early |
|---|---|---|---|
| About one hour | Does not expire on a clock for a published app | An OAuth app still in Testing status issues refresh tokens that expire after 7 days. Also: the user revoking access from their Google account, a password change where sensitive scopes are involved, and the per-user-per-client limit that silently retires the oldest tokens when a new one is issued. | |
| Microsoft Entra ID | Roughly an hour, and the exact figure is randomized by Microsoft | Governed by inactivity windows and session policy rather than a lifetime you can type into a box; the old configurable-lifetime policies were withdrawn | Password reset (AADSTS50173), an admin revoking sessions, and Conditional Access with continuous access evaluation cutting a token mid-life when risk or location changes. |
| Slack, with token rotation enabled | 12 hours | Refresh token is single use; each refresh returns a new one that replaces it | Missing a rotation cycle, because the old refresh token is already spent. Apps that store the refresh token in a place they cannot write back to break here permanently. |
| Meta (Facebook, Instagram) | Short-lived user token, about an hour | Long-lived user token, about 60 days | The user changing their Facebook password, an admin removing the app, or a Page role change. Page tokens derived from a long-lived user token behave differently again, and the details depend on how the app requested them. |
| HubSpot | 30 minutes | Refresh token does not expire on a clock | Uninstalling the app from the portal, or an admin disconnecting the integration. |
Two things fall out of that table. The first is that "token refresh failed" is a background event, so by the time a human sees an error the credential has usually been dead for hours. The second is the Google testing-mode trap, which we have now met on three separate client systems: the builder never moved the OAuth consent screen from Testing to Published, everything worked beautifully for a week, and the whole thing fell over on day eight. If your integration dies on a suspiciously round schedule, check the app's publishing status before you check anything else.
The monitor that turns this into a Tuesday
Expiry is predictable in a way most failures are not, so it is worth a small piece of infrastructure. The workflow attached to this page runs on a schedule, makes one cheap authenticated call per connected service, and messages you the moment any of them stops answering correctly.
It checks the response body rather than the status code, which is the part most home-made monitors get wrong. A Slack call returning ok: false with a 200 has to count as a failure, or the monitor develops the same blind spot as the workflow it is watching. Import it, point the notification node at a channel a human genuinely reads, and add one line per credential you care about.
Set the schedule tighter than your shortest token life. Daily is right for most stacks. If you have Slack rotation in the mix, hourly costs almost nothing and catches a broken rotation loop before the 12 hour window closes. The same instinct applies to everything else you run, which is roughly the argument in why AI automations die in production.
You can probably fix this afternoon's outage yourself
Most credential failures do not need an agency. If you can get into the accounts, this is genuinely an afternoon of work, and I would rather you did it than paid us.
- Open the credential in n8n or Make, click through the OAuth consent screen again, and tick every permission it asks for. Do not skip one because it looks excessive; that is what produces the 403 you will be back for tomorrow.
- If it is a Google app, go to the Google Cloud console, find the OAuth consent screen, and check whether it says Testing. Publishing it is two clicks and removes the 7 day ceiling.
- Move the connection off the personal account of whoever set it up and onto a service account or a shared company mailbox. Three of the six rescues we have scoped began with the original builder gone and every credential still in his name, which turns a ten minute reconnect into a legal conversation.
- Find the alert destination and send a test message to it right now. Half the systems we open have working alerts pointed at somewhere nobody looks, and the fix costs nothing.
- Write down which human account each integration is attached to. One page. The handover checklist covers what else belongs on it.
If that sequence gets your runs green again, you are done and you owe nobody anything.
When paying someone is the cheaper option
Call us when the reconnection will not hold for more than a few days, when you cannot tell how many silent partial failures have already gone through, when an admin keeps refusing a scope the integration needs, or when the accounts belong to somebody who stopped answering your email in March. Repeated expiry on the same connection usually means something structural, and that also tends to be true when the same flow shows up in an n8n workflow that keeps failing for reasons that never quite resolve.
We start with a $500 written diagnostic delivered in 3 business days, credited in full against whatever work follows. Small fixes run $750 to $1,500, a standard rescue is $1,500 to $4,000, and a full rebuild is $4,000 to $10,000. Everything carries a 30-day bug warranty. The diagnostic is yours to keep and to take to any other vendor for a second quote, which is the point of putting it on paper.
We run six systems in production ourselves, including our own voice receptionist, so the monitoring described on this page is what keeps our own phone answering on a Sunday. If you want somebody else to own that problem, start at the rescue service or tell us what broke. If the symptom is broader than credentials, the general stopped-working page is the better place to begin.
What each band includes
Every band below is a fixed price agreed before any invoice. The diagnostic comes off the repair in full, so if you go ahead you have paid nothing extra for the reading.
| Band | Price | Time |
|---|---|---|
| Written diagnosticWe read the workflows, logs, and prompts. Written root-cause report and a fixed repair quote. Credited in full against the fix. | $500 | 3 business days |
| Small fixOne clear failure: a broken integration, a bad prompt, a missing retry. When the diagnostic shows a minor break, you pay the minor price. | $750-$1,500 | 2-5 days |
| Standard rescueWhere most rescues land. Several failure points or a fragile architecture: root-cause fixes, error handling, alerts, and a trail you can audit. | $1,500-$4,000 | 1-2 weeks |
| RebuildOnly when repairing costs more than starting over. The diagnostic says so in writing, with both numbers, before you decide. | $4,000-$10,000 | 2-4 weeks |
Full detail on the rescue page, and every other number we charge is on the pricing page.
Other symptoms we have written up
Longer reading
Send us the execution log and we will tell you what broke
A thirty-minute call and a price at the end of it. If the fix is small enough to do yourself, we will say so on the call.