plugin-multi-tenant: `isGlobal` collections redirect tenant-scoped users to `/admin` since 3.93.0 (GlobalViewRedirect prop-name mismatch)

Open Beginner friendly
#19,420 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@jmtorres is already working on this.

Since Nov 6, 2026.

  • #19421 by @jmtorres — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
next.js, typescript

Research direction

Read packages/plugin-multi-tenant/src/index.ts and packages/plugin-multi-tenant/src/components/GlobalViewRedirect/index.ts to compare the registered server prop names with the component’s arguments. Reproduce with the linked repro using a tenant-scoped user who cannot access all tenants. Done when that user opening an isGlobal collection reaches its tenant’s document instead of /admin.

Written by the indexing model from the issue text.

Description

Bug plugin: multi-tenant
Describe the Bug

Since 3.93.0, a user who has access to some tenants (but not all tenants) can't open an isGlobal: true collection. The list view, a direct document URL and the dashboard card all load and then bounce to /admin. Users for whom userHasAccessToAllTenants returns true are not affected.

The cause is a prop-name mismatch between the plugin's registration of GlobalViewRedirect and the component.

packages/plugin-multi-tenant/src/index.ts registers it with tenantsArrayFieldName / tenantsArrayTenantFieldName:

{
  path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',
  serverProps: {
    globalSlugs: globalCollectionSlugs,
    tenantFieldName,
    tenantsArrayFieldName,
    tenantsArrayTenantFieldName,
    tenantsCollectionSlug,
    useAsTitle: tenantCollection.admin?.useAsTitle || 'id',
    userHasAccessToAllTenants,
  },
}

packages/plugin-multi-tenant/src/components/GlobalViewRedirect/index.ts reads tenantArrayFieldName / tenantArrayTenantFieldName (no "s"), so both values arrive as undefined:

type Args = {
  // ...
  tenantArrayFieldName: string
  tenantArrayTenantFieldName: string
  // ...
}

const redirectRoute = await getGlobalViewRedirect({
  // ...
  tenantsArrayFieldName: args.tenantArrayFieldName,
  tenantsArrayTenantFieldName: args.tenantArrayTenantFieldName,
  // ...
})

The mismatch is older than 3.93.0, but it rarely mattered before then. Up to 3.92.x, getGlobalViewRedirect used the payload-tenant cookie and only called getTenantOptions when that cookie was missing. Commit 43c32b194 ("fix: constrain global view redirects to available tenants", shipped in 3.93.0) now calls getTenantOptions on every request. For a user without all-tenants access, getTenantOptions reads user[undefined], finds no tenants, and the helper falls through to its "no tenants were found" branch, which redirects to the admin root.

The same mismatch is on main (v4).

Link to the code that reproduces this issue

https://github.com/jmtorres/payload-multi-tenant-global-redirect

Reproduction Steps
  1. Clone the repro (it uses the blank template on SQLite with Payload 3.93.0), then run cp .env.example .env, pnpm install, pnpm payload run src/seed.ts and pnpm dev.
    • Plugin config: collections: { settings: { isGlobal: true } }, userHasAccessToAllTenants: (user) => user.roles.includes('super-admin').
    • The seed creates tenants A and B, [email protected] (super-admin), [email protected] (role user, tenant A only) and one settings doc for tenant A. All passwords are test.
  2. Log in at /admin as [email protected] / test.
  3. Open Settings (/admin/collections/settings).

Expected: a redirect to tenant A's settings doc (/admin/collections/settings/1).
Actual: the page loads, then the browser lands on /admin (the dashboard).

Logging in as [email protected] lands on /admin/collections/settings/1 as expected.

Starting the app with WORKAROUND=1 pnpm dev adds the misspelled names (tenantArrayFieldName: 'tenants', tenantArrayTenantFieldName: 'tenant') to the registered serverProps (see src/payload.config.ts). The editor then lands on the doc, which confirms the cause.

Suggested fix: rename the component's props to tenantsArrayFieldName / tenantsArrayTenantFieldName to match the registration. PR incoming.

Which area(s) are affected?

plugin: multi-tenant

Environment Info
payload: 3.93.0
@payloadcms/plugin-multi-tenant: 3.93.0
@payloadcms/db-sqlite: 3.93.0
next: 16.3.3
react: 19.2.6
Node: 24.22.2
pnpm: 12.16.1
OS: macOS (darwin arm64)

Also present on main @ e71e91c66.
Dominant language
TypeScript
Stars
45.6k
Forks
4.4k
Avg merge
1d 23h
Merged PRs (30d)
237

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from payloadcms/payload

All issues in payloadcms/payload

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.