nextauthjs/next-auth · error · TypeError

Missing refresh_token

Error message

Missing refresh_token

What it means

FusionAuth's token refresh logic throws a TypeError when it needs to refresh an expired access_token but the stored token object has no refresh_token property. Without a refresh token there is no way to obtain a new access token, so the provider fails fast. This typically indicates the initial token response did not include a refresh token (or it was not persisted).

Source

Thrown at packages/core/src/providers/fusionauth.ts:181

  // the ability to call API's externally that rely on JWT tokens.
  callbacks: {
    async jwt(params) {
      const { token, user, account } = params;
      if (account) {
        // First-time login, save the `access_token`, its expiry and the `refresh_token`
        return {
          ...token,
          ...account,
        };
      } else if (
        token.expires_at &&
        Date.now() < (token.expires_at as number) * 1000
      ) {
        // Subsequent logins, but the `access_token` is still valid
        return token;
      } else {
        // Subsequent logins, but the `access_token` has expired, try to refresh it
        if (!token.refresh_token) throw new TypeError('Missing refresh_token');

        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,
              }),
            }
          );

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Request the offline access scope in the FusionAuth authorization request so a refresh_token is issued (authorization: { params: { scope: "offline_access" } })
  2. Verify FusionAuth application settings allow refresh tokens and inspect the initial token response
  3. Force a full re-login (clear cookies/session) so a fresh token set including refresh_token is obtained
  4. Audit your adapter/callback code to ensure token.refresh_token is persisted, not stripped

Example fix

// before
import FusionAuth from "@auth/core/providers/fusionauth"
providers: [FusionAuth({ clientId, issuer })] // no offline scope
// after
providers: [
  FusionAuth({
    clientId: process.env.AUTH_FUSIONAUTH_ID,
    clientSecret: process.env.AUTH_FUSIONAUTH_SECRET,
    issuer: process.env.AUTH_FUSIONAUTH_ISSUER,
    authorization: { params: { scope: "offline_access" } },
  })
]
Defensive patterns

Strategy: type-guard

Validate before calling

// before relying on refresh, check the stored token
if (!token.refresh_token) {
  // force full re-authentication instead of attempting refresh
  await clearSession()
}

Type guard

function hasRefreshToken(t: { refresh_token?: string | null }): t is { refresh_token: string } {
  return typeof t.refresh_token === 'string' && t.refresh_token.length > 0
}

Try / catch

try {
  await refreshed = refreshAccessToken(token)
} catch (e) {
  if (e instanceof TypeError && e.message === 'Missing refresh_token') {
    // cannot refresh: invalidate session and redirect user to sign in again
    await signOut()
  }
}

Prevention

When it happens

Trigger: On a subsequent login the check Date.now() < token.expires_at * 1000 is false (access token expired), and !token.refresh_token is true — i.e. the persisted token from the initial OAuth callback lacked a refresh_token because the original token request omitted the offline_access scope or the token was stored stripped of the field.

Common situations: Hitting this after a session outlives the access token lifetime; after changing FusionAuth application settings so refresh tokens are no longer issued; or when a custom callback/adapter drops refresh_token when saving tokens (e.g. typed as optional and serialized as undefined).

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/c30e8e0628ab1570. Report an issue: GitHub.