payloadcms/payload · error · Error

Cannot refresh token: user not authenticated

Error message

Cannot refresh token: user not authenticated

What it means

The `refresh` server function calls `payload.auth({ headers })`; if the returned `result.user` is null/undefined it throws this. There is no authenticated session to refresh: the request carried no valid (or already expired/destroyed) auth token.

Source

Thrown at packages/payload/src/auth/serverFunctions/refresh.ts:30

  config: MaybePromise<SanitizedConfig>
  serverAdapter: ServerAdapter
}

/**
 * Refreshes the current user's auth token and rewrites the cookie through the
 * supplied `serverAdapter`, so the function is framework-agnostic; each adapter
 * binds its own.
 */
export async function refresh({
  config,
  serverAdapter,
}: RefreshArgs): Promise<{ message: string; success: boolean }> {
  const payload = await getPayload({ config, cron: true })
  const headers = await serverAdapter.getHeaders()
  const result = await payload.auth({ headers })

  if (!result.user) {
    throw new Error('Cannot refresh token: user not authenticated')
  }

  const existingCookie = await getExistingAuthToken({
    cookiePrefix: payload.config.cookiePrefix,
    serverAdapter,
  })

  if (!existingCookie) {
    return { message: 'No valid token found to refresh', success: false }
  }

  const collection: CollectionSlug | undefined = result.user.collection
  const collectionConfig = payload.collections[collection]

  if (!collectionConfig?.config.auth) {
    throw new Error(`No auth config found for collection: ${collection}`)
  }

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Guard the `refresh` call behind a session/cookie presence check.
  2. Verify `serverAdapter.getHeaders()` forwards the auth cookie.
  3. If the token expired, call `login` first to obtain a fresh token before refreshing.

Example fix

// before
await refresh({ config, serverAdapter })
// after
const authed = await payload.auth({ headers: await serverAdapter.getHeaders() })
if (!authed.user) {
  await login({ collection: 'users', config, email, password, serverAdapter })
} else {
  await refresh({ config, serverAdapter })
}
Defensive patterns

Strategy: validation

Validate before calling

async function hasSessionCookie(serverAdapter) {
  const headers = await serverAdapter.getHeaders()
  const cookie = headers.get('cookie') ?? ''
  return /payload-token=/.test(cookie)
}
// before refresh:
if (!(await hasSessionCookie(serverAdapter))) await redirectToLogin()

Type guard

function isAuthenticated(authResult): authResult is { user: Record<string, unknown> } {
  return !!authResult?.user
}

Try / catch

try {
  await refresh({ config, serverAdapter })
} catch (e) {
  if (e instanceof Error && /not authenticated/.test(e.message)) {
    await redirectToLogin()
  } else {
    throw e
  }
}

Prevention

When it happens

Trigger: Calling `refresh({ config, serverAdapter })` when `serverAdapter.getHeaders()` returns headers with no `payload-token` cookie, an expired token, or a token whose user/session was deleted. The auth lookup resolves to no user.

Common situations: Token expired and the client did not re-login; an SSR adapter stripped the cookie; the user was deactivated/deleted; refresh invoked on an already-logged-out session.

Understand the failure class

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/47de5786abb18eb3. Report an issue: GitHub.