payloadcms/payload · error · UnauthorizedError

Unauthorized, you must be logged in to make this request.

Error message

Unauthorized, you must be logged in to make this request.

What it means

The MCP access handler authenticates with the Authorization header while disabling autologin. If a credential was presented (hasAuthorization) but req.payload.auth resolved to no user, it throws UnauthorizedError — the token/API key is invalid, expired, or unrecognized.

Source

Thrown at packages/plugin-mcp/src/endpoint/access.ts:40

  const pluginConfig = getPluginConfig({ config: req.payload.config })

  if (pluginConfig.overrideGetAuthorizedMCP) {
    return await pluginConfig.overrideGetAuthorizedMCP({
      overrideAccess,
      pluginConfig,
      req,
    })
  }

  if (req.headers) {
    const headers = new Headers(req.headers)
    const hasAuthorization = headers.has('Authorization')

    headers.set('DisableAutologin', 'true')
    req.user = (await req.payload.auth({ headers, req })).user

    if (hasAuthorization && !req.user) {
      throw new UnauthorizedError(req.t)
    }
  }

  return {
    items: await filterMCPItems({
      items: pluginConfig.items,
      overrideAccess,
      req,
    }),
    overrideAccess,
  }
}

export const filterMCPItems = async ({
  items,
  overrideAccess,
  req,
}: {

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Refresh the token / regenerate the API key and resend the Authorization header.
  2. Verify the auth header scheme matches the configured Payload auth strategy.
  3. Confirm the user still exists and the credential is valid via req.payload.auth in isolation.

Example fix

// before: expired token
fetch('/mcp', { headers: { Authorization: 'JWT expired.token' } })
// after: fresh token
fetch('/mcp', { headers: { Authorization: `JWT ${freshToken}` } })
Defensive patterns

Strategy: validation

Validate before calling

const headers = new Headers(req.headers)
if (headers.has('Authorization')) {
  const { user } = await req.payload.auth({ headers, req })
  if (!user) throw new Error('Credential present but no user — refresh the token/API key')
}

Try / catch

try {
  await getAuthorizedMCP({ overrideAccess, req })
} catch (err) {
  if (err instanceof UnauthorizedError) {
    // refresh credential and retry, or prompt re-login
  } else throw err
}

Prevention

When it happens

Trigger: Expired or revoked JWT/API key; wrong auth scheme; token for a different Payload instance; API key that was rotated.

Common situations: Long-lived MCP client session whose token expired mid-session; API key rotated without updating clients; misconfigured auth header (e.g. basic instead of bearer).

Understand the failure class

Related errors


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