payloadcms/payload · error · APIError

Unauthorized

Error message

Unauthorized

What it means

Thrown by the multi-tenant plugin's `getTenantOptionsEndpoint` handler as a 401 when the request to `/populate-tenant-options` has no authenticated user. The endpoint exists to return the tenant list the current user can access, so an anonymous request has nothing to filter.

Source

Thrown at packages/plugin-multi-tenant/src/endpoints/getTenantOptionsEndpoint.ts:26

export const getTenantOptionsEndpoint = ({
  tenantsArrayFieldName,
  tenantsArrayTenantFieldName,
  tenantsCollectionSlug,
  useAsTitle,
  userHasAccessToAllTenants,
}: {
  tenantsArrayFieldName: string
  tenantsArrayTenantFieldName: string
  tenantsCollectionSlug: string
  useAsTitle: string
  userHasAccessToAllTenants: Required<MultiTenantPluginConfig>['userHasAccessToAllTenants']
}): Endpoint => ({
  handler: async (req) => {
    const { payload, user } = req

    if (!user) {
      throw new APIError('Unauthorized', 401)
    }

    const tenantOptions = await getTenantOptions({
      payload,
      tenantsArrayFieldName,
      tenantsArrayTenantFieldName,
      tenantsCollectionSlug,
      useAsTitle,
      user,
      userHasAccessToAllTenants,
    })

    return new Response(JSON.stringify({ tenantOptions }))
  },
  method: 'get',
  path: '/populate-tenant-options',
})

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Ensure the caller is authenticated before requesting tenant options (gate the UI on login state)
  2. Pass the session cookie / API key with the request
  3. If the picker must render for anonymous users, fetch tenant options only after login succeeds

Example fix

// before
fetch('/api/tenants/populate-tenant-options')
// after
fetch('/api/tenants/populate-tenant-options', { credentials: 'include' })
Defensive patterns

Strategy: validation

Validate before calling

// Only fetch tenant options when a user is present
if (!currentUser) return []
return await fetch('/api/tenants/populate-tenant-options', { credentials: 'include' }).then(r => r.json())

Try / catch

const res = await fetch('/api/tenants/populate-tenant-options', { credentials: 'include' })
if (res.status === 401) { await relogin(); return fetchOriginal() }

Prevention

When it happens

Trigger: Hitting the `/populate-tenant-options` GET endpoint without a session; calling it from an unauthenticated frontend before login; expired session cookie; server-to-server fetch that omits credentials.

Common situations: Frontend tenant selector dropdown firing before the login flow completes; cookie blocked by SameSite/secure; a public page that mistakenly mounts the tenant picker without gating on auth.

Understand the failure class

Related errors


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