Spurlock Studios
Contact
OAuth Tokens Will Expire: Stop Silent 401 Loops in Production

Your automation started failing with 401 / expired token because the access token died and n8n never got a refresh signal it understands — or the refresh token itself is gone — and nothing paused the workflow, so it looped quietly until a human noticed the CRM went dark.

n8n’s generic OAuth2 path refreshes reactively when the HTTP status matches tokenExpiredStatusCode (default 401). It does not proactively refresh from expires_in on every vendor. Webhook HMAC and signature verification are a different control — see Webhook security. This post owns credential lifecycle. Spine: Production n8n handbook.

The short answer

  • Reactive refresh — on matching status (default 401), n8n refreshes and retries. Wrong status or body-only errors → no refresh.
  • Reconnect “fixes” it for a day — you minted a fresh access token; the underlying refresh/consent problem remains.
  • Production prefers service accounts / workspace apps over a founder’s personal Google login.
  • Auth failures are pause-class incidents — stop the storm, fix the credential, then resume.
  • Credentials live in n8n Credentials (or a secret manager) — never in sticky notes on the canvas.

How n8n refreshes OAuth in practice

StepBehavior
Request with access tokenHTTP Request / node calls the API
Response status == tokenExpiredStatusCodeTrigger refresh (or client-credentials fetch)
Refresh succeedsPersist new oauthTokenData; retry the request
Refresh fails / wrong statusError surfaces; no magic reconnect

Default expired status is 401. Some APIs return 403 on expiry — set tokenExpiredStatusCode accordingly on the generic OAuth2 credential (field added in recent n8n releases; see credential UI / OAuth2Api credentials).

Known gap: vendors that return HTTP 200 with an error body when the token is dead never trip reactive refresh. That is working-as-designed today for generic OAuth2 (n8n issue #32423). You must detect those body codes yourself or reconnect on a schedule — do not assume expires_in alone saves you.

Why some APIs never trigger refresh

Decision list when “auto refresh” fails:

  1. Does the API return 401 (or your configured code) on expired access token?
  2. Did the provider issue a refresh token? Client Credentials often does not — by design you re-fetch, not refresh.
  3. Was the refresh token single-use / rotated and an older worker overwrote storage?
  4. Did scopes change so refresh is rejected until re-consent?
  5. Is the app in testing mode with tokens that expire in days (common on Google Cloud OAuth apps still in Testing)?

If (1) is no, fix detection or vendor choice before you blame n8n.

Personal OAuth vs service accounts

PatternUse whenFailure mode
Personal Google / Microsoft OAuthPrototypes, personal productivityOffboarding, password reset, 2FA change, “Testing” token expiry
Workspace / company OAuth app + shared mailboxTeam ops with consent policyStill tied to human approval if mis-scoped
Service account / server-to-serverProduction backends that support itKey rotation discipline required
Vendor API key / PATWhen OAuth is optionalKey leak blast radius — rotate on schedule

Production rule: if the workflow outlives the employee’s laptop, it cannot depend on that employee’s interactive OAuth.

Failure mode: reconnect looks fixed for a day

What breaks: overnight 401s. Operator reconnects the credential. Morning looks green. Tomorrow (or next week) the same loop returns.

What actually happened: reconnect minted a new access token (and maybe a short-lived refresh). The root cause — Testing app, revoked refresh, wrong tokenExpiredStatusCode, personal account policy — is untouched.

What it costs: silent data gaps, duplicate “fixes,” and trust erosion when the board asks why the pipeline “keeps dying.”

What you do instead: treat reconnect as incident mitigation, then run the root-cause checklist below before you call it closed.

Incident response when credentials die overnight

Runbook (paste into your ops doc):

  1. Confirm — error is 401/403/auth, not schema or 429.
  2. Pause affected production workflows (and siblings sharing the credential).
  3. Alert once with credential name, workflows, first/last failure time — not a page per execution.
  4. Diagnose — refresh token present? status code mismatch? personal account? Google Testing mode?
  5. Rotate / reconnect using the correct account type; verify with a single staging or pinned-data run.
  6. Resume workflows deliberately; watch the next scheduled/webhook cycle.
  7. Write the postmortem line — root cause + permanent fix (service account, status code, monitoring).

Auth storms without pause are how you burn rate limits and fill DLQ with the same poison.

tokenExpiredStatusCode — what to set

On generic OAuth2 credentials:

Token Expired Status Code: 401   # default
# set to 403 if your API uses 403 for expired access tokens

Checklist:

  • Document the vendor’s real expiry status from a captured response
  • Set the credential field to match
  • Prove refresh in staging by forcing expiry (short-lived token or revoked access)
  • If vendor returns 200 + body error, add an explicit IF/Code branch — do not wait for n8n to guess

Rotate secrets without downtime

ApproachStepsNotes
Dual credential cutoverCreate new credential → point staging → flip production nodes → revoke oldBest for API keys / second OAuth app
In-place reconnectPause → reconnect → single test → resumeFine for true OAuth refresh repair
Env / credential overwriteInject via supported overwrite mechanismsKeep encryption key/backup process intact

Never paste client secrets into Slack. Never leave the old consumer key active “just in case” without a revoke date.

Offboarding and personal Google accounts

How companies break:

  1. Intern connects Gmail/Sheets with their user OAuth.
  2. Intern leaves; refresh is revoked.
  3. Nobody owns the credential; workflows stay active.
  4. Failures look like “n8n is flaky” for a week.

Controls:

  • Credential owner field in your runbook (human name + backup)
  • Ban personal accounts on SEV1 workflows
  • Offboarding checklist includes n8n credential audit
  • Alert on rising 401 rate per credential, not only per workflow

Where credentials should live

PlaceAllowed?
n8n Credentials storeYes — default
Secret manager → injected at deployYes — for self-hosted discipline
Canvas sticky notes / Set node hardcodesNo
Shared Google Doc “for the team”No
Git repoNo (unless encrypted vault pattern you already operate)

If someone needs a value to debug, grant time-boxed access to the credential UI — do not copy secrets into the graph.

Monitoring that catches quiet expiry

Minimum signals:

  1. Error workflow classifies auth failures.
  2. Threshold alert: N auth failures in M minutes → page + auto-pause candidate list.
  3. Weekly credential inventory: owner, type (personal vs service), last successful refresh/use.
  4. Staging job that exercises each critical credential on a schedule.

Quiet is the enemy. Green checkmarks on an old execution do not prove tomorrow’s refresh works.

Google “Testing” mode and short-lived grants

If your OAuth client is still in Testing in the provider console (common on Google Cloud), refresh tokens can expire on a short calendar (often days, depending on current Google policy for test users) even when n8n’s reactive refresh is configured correctly. Production automations need a published / production-grade app consent posture, or a non-user grant (service account) where the API allows it.

Checklist when Google-connected workflows die weekly:

  • OAuth client publishing status is Production (or equivalent), not Testing
  • Test users list is not the only path to a token
  • Scopes match what production nodes actually call
  • Credential owner is a company account, not a contractor personal Gmail

Reconnect without leaving Testing is the “fixed for a day” pattern with a calendar.

Credential inventory template

Copy into your runbook:

FieldExample
Credential name in n8nprod-google-sheets-ops
Vendor / appGoogle Sheets — company Cloud project
Grant typeOAuth (workspace) / service account / PAT
Owner + backupJamie / Alex
Workflows using itinvoice-sync, lead-enrichment
SEV if deadSEV1 / SEV2
tokenExpiredStatusCode401
Last proven refresh2026-04-20 staging job
Offboarding riskPersonal? yes/no

Review monthly. Orphans are incidents waiting for a Friday.

Pair auth failures with the error workflow

Auth is a first-class error class next to schema and rate-limit:

  1. Error Trigger / workflow-level error handler catches the failure.
  2. Classify errorClass=auth.
  3. Write DLQ row with credential name (not the secret).
  4. Notify with deep link + “paused?” recommendation.
  5. Optional: auto-disable a tagged set of workflows after N auth failures.

Do not page for every enrichment 401 if you already paused the critical path — mute siblings intentionally.

FAQ

Why does reconnecting “fix” it for a day?

Reconnect issues a fresh access token (and often a new refresh grant), so the next hours succeed. If the app is in Testing mode, the refresh policy is wrong, or the account will be revoked again, the same outage returns. Fix the root cause after the reconnect.

What is tokenExpiredStatusCode about?

It is the HTTP status n8n treats as “access token expired — refresh and retry” on generic OAuth2 credentials. Default is 401. Set it to 403 (or another code) when that is what your API returns on expiry; otherwise refresh never runs.

How do I rotate secrets without downtime?

Prefer dual credentials: create the new credential, validate in staging, flip production node mappings, then revoke the old secret. For OAuth reconnects, pause the workflows, reconnect, smoke-test once, then resume.

Should I pause workflows on auth failures?

Yes for production paths that would otherwise spam 401s. Pause (or disable) siblings sharing the dead credential, fix once, then resume. Leaving them active turns a credential incident into a rate-limit and alert-fatigue incident.

How do offboarding and personal Google accounts break companies?

Personal OAuth dies when the human leaves, resets a password, or loses consent. Production workflows should use service accounts or company-owned apps with a named owner and an offboarding audit that includes n8n credentials.

Where should credentials live — canvas notes?

Never on the canvas. Use the n8n Credentials store or a secret manager injection path. Sticky notes and Set-node secrets become leaks and unrotatable debt.

CTA

Treat OAuth like a production dependency with an owner — reconnect is mitigation, not a strategy.

Keep the handbook open for the rest of the spine. For a credential lifecycle review on your n8n estate, use automation or book a call.

Book the audit