nextauthjs/next-auth · error · Error
Failed to refresh token
Error message
Failed to refresh token
What it means
Thrown when the POST to FusionAuth's /oauth2/token endpoint (grant_type=refresh_token) returns a non-OK response. The stored refresh token could not be exchanged for a new access token, so the provider gives up rather than continuing with an expired session. The failure is silent about the API body here — check the refresh response separately for details.
Source
Thrown at packages/core/src/providers/fusionauth.ts:201
try {
const refreshResponse = await fetch(
`${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: process.env.AUTH_FUSIONAUTH_CLIENT_ID!,
client_secret: process.env.AUTH_FUSIONAUTH_CLIENT_SECRET!,
grant_type: 'refresh_token',
refresh_token: token.refresh_token as string,
}),
}
);
if (!refreshResponse.ok) {
throw new Error('Failed to refresh token');
}
const tokensOrError = await refreshResponse.json();
if (!refreshResponse.ok) throw tokensOrError;
const newTokens = tokensOrError as {
access_token: string;
expires_in: number;
refresh_token?: string;
};
return {
...token,
access_token: newTokens.access_token,
expires_at: Math.floor(Date.now() / 1000 + newTokens.expires_in),
// Some providers only issue refresh tokens once, so preserve if we did not get a new one
refresh_token: newTokens.refresh_tokenView on GitHub (pinned to a1a16a5a77)
Solutions
- Log the refresh response body/status to identify the exact rejection reason
- Verify AUTH_FUSIONAUTH_ISSUER, client id/secret env vars match your FusionAuth application
- If the refresh token is expired/revoked, clear the session and force re-authentication
- Confirm the FusionAuth application has refresh tokens enabled and reasonable TTLs
Example fix
// before issuer: process.env.AUTH_FUSIONAUTH_ISSUER // undefined in prod -> token endpoint 404 // after // .env.production // AUTH_FUSIONAUTH_ISSUER=https://auth.example.com issuer: process.env.AUTH_FUSIONAUTH_ISSUER!, // set and verify with a health check // and handle refresh failures by ending the session so the user re-authenticates
Defensive patterns
Strategy: retry
Validate before calling
// validate env before provider construction
if (!process.env.AUTH_FUSIONAUTH_ISSUER || !process.env.AUTH_FUSIONAUTH_ID || !process.env.AUTH_FUSIONAUTH_SECRET) {
throw new Error('FusionAuth env vars (ISSUER/ID/SECRET) are not fully configured')
} Type guard
function isTokenRefreshable(t: { refresh_token?: unknown }): t is { refresh_token: string } {
return typeof t.refresh_token === 'string' && t.refresh_token.length > 0
} Try / catch
try {
const res = await fetch(`${issuer}/oauth2/token`, { method: 'POST', ... })
if (!res.ok) {
const body = await res.text()
console.error(`FusionAuth refresh failed (${res.status}):`, body)
if (res.status >= 500) /* retry with backoff */
else /* expired/revoked: end session, force re-auth */
}
} catch (e) { /* network error: retry with backoff */ } Prevention
- Set refresh token TTLs in FusionAuth generously enough for your session lifetime
- Keep AUTH_FUSIONAUTH_ISSUER/client credentials in sync across environments
- On refresh failure, clear the session so the user re-authenticates instead of looping
- Retry 5xx/429 with exponential backoff; never retry 4xx auth rejections
When it happens
Trigger: Calling refresh with token.refresh_token set but FusionAuth rejects it: refresh token expired or revoked (user logged out, password change, tenant policy), wrong AUTH_FUSIONAUTH_ISSUER/client credentials, network/API outage, or the token endpoint URL changed. Any !refreshResponse.ok triggers the throw.
Common situations: Users returning after long inactivity past the refresh token TTL; environments where AUTH_FUSIONAUTH_ISSUER points at the wrong host; FusionAuth admin revoking sessions; rotating client secrets without redeploying.
Related errors
- Missing refresh_token
- Account not created
- Missing or invalid provider account
- Provider not supported
- The account is already associated with another user
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/2b1f1c62f54689fe.
Report an issue: GitHub.