Salesforce refresh token expired: policies and fixes
When an integration starts failing with invalid_grant and “expired access/refresh token”, the connected app's refresh token policy has expired its token. Salesforce sends no warning first. Check the policy, re-authorise the integration user, and choose an inactivity-based policy rather than a fixed lifetime for anything unattended.
Checked against Salesforce documentation on 11 October 2026.
What breaks
The integration's next attempt to get a session fails with:
{"error":"invalid_grant","error_description":"expired access/refresh token"}The policy is only checked when the refresh token is used, so nothing fails until the integration next tries to renew its session. A nightly job usually fails at 2am with nobody watching.
The policies
Setup, Connected Apps, Manage Connected Apps, Edit, OAuth Policies, Refresh Token Policy:
| Option | Metadata value |
|---|---|
| Refresh token is valid until revoked (default) | infinite |
| Immediately expire refresh token | zero |
| Expire refresh token if not used for n | specific_inactivity:7:DAYS |
| Expire refresh token after n | specific_lifetime:30:DAYS |
External Client Apps express the same choices as refreshTokenPolicyType (Infinite, SpecificInactivity, SpecificLifetime, Zero) with refreshTokenValidityPeriod and refreshTokenValidityUnit (Days or Hours, among others).
If your org turned on “Limit Idle Refresh Token Time-to-Live (TTL) to 30 Days”, a policy of “valid until revoked” behaves as 30 days of inactivity, but the metadata is not updated for you. Set it to specific_inactivity:30:DAYS yourself so source control matches what the org does.
See who holds tokens
- Setup, Connected Apps OAuth Usage. The user count opens a list with first connected, last used and use count.
- Through the API, query
OauthToken(needs Customize Application to see every user's tokens):
SELECT AppName, UserId, LastUsedDate, UseCount FROM OauthToken ORDER BY LastUsedDate
A query returns at most 2,500 rows, so page through larger orgs with OFFSET.
Fix it
- Re-authorise the integration user to get a new refresh token.
- If the expiry was not intended, change the policy. For unattended integrations, prefer “expire if not used for n” over “expire after n”, which kills a token on a fixed date even when it is used daily.
- Revoke tokens you no longer trust from the OAuth Usage page.
Changes since 2025
- From September 2025, connected apps that are not installed in the org are blocked for new users, who see
error=invalid_clientwith “app must be installed into org”. On Connected Apps OAuth Usage, click Install for trusted apps and Block for the rest. - From Spring '26, new connected apps cannot be created without Salesforce Support. Existing ones keep working; new integrations use External Client Apps.
Common mistakes
- Using “expire after n” for an unattended integration.
- Requesting a new refresh token on every run. Each app allows five approvals per user, and the sixth silently revokes the oldest.
- Turning on the 30-day idle limit without updating the policy metadata.
- Ignoring the Install buttons on OAuth Usage, which leaves new users of those apps blocked.
Or let OrgPulse watch it
OrgPulse checks your org every day and alerts you by email or Slack before these dates arrive. It only reads your org, and the first org is free.
Sources
- Manage OAuth access policies for a connected app
- OAuth flow errors
- Uninstalled connected app usage restrictions
- Limit idle refresh token time-to-live to 30 days
- Manage OAuth-enabled connected apps access
- Connected app creation changes
- Object Reference (OauthToken), PDF
- Metadata API Developer Guide (ConnectedApp, External Client App policies), PDF