Skip to content

Role column renders "<No Role>" in Users list view on Payload 3.84 (afterRead over-populates role; roleFieldConfig is a no-op) #77

Description

@antonbrams

Summary

On Payload 3.84.1 with gatekeeper 1.1.0, the role column in the Users
list view renders empty (<No Role>) for every user, even a super admin whose
role clearly exists and has a label. The role assignment itself works correctly —
only the list-view display is broken.

Two things combine here, and the second one removes the obvious workaround.

Environment

  • payload-gatekeeper: 1.1.0 (latest)
  • payload: 3.84.1
  • DB: MongoDB
  • (Note: gatekeeper's own docs screenshot was taken against Payload 3.52.0, where
    this renders fine. I couldn't pin the exact Payload change between 3.52 and
    3.84, so I'm reporting observed behavior on 3.84.1.)

Bug 1 — list cell can't render the populated role object

createAfterReadHook (src/hooks/afterReadHook.ts) replaces the role ID with
a fully-populated role object on every read:

if (doc?.role && (typeof doc.role === 'string' || typeof doc.role === 'number')) {
  doc.role = await req.payload.findByID({ collection: getRolesSlug(), id: ..., depth: 0 })
}

Payload's default RelationshipCell only builds its display value from a plain ID
(string/number) or a { relationTo, value } pair. A fully-populated object
matches neither branch, so its formattedValues array stays empty, it never calls
getRelationships, and it falls back to general:noLabel<No Role>.

Verified directly: GET /api/users?depth=0 returns role as a full object (not an
ID), confirming the hook fires for list/collection reads on 3.84.

Importantly, the populated object can't just be droppedreq.user.role.permissions
is exactly what createCollectionAccess and createUIVisibilityCheck read on every
request, so removing/guarding the population would break access + sidebar visibility.
The only safe layer to fix is display.

Bug 2 — roleFieldConfig is documented but never applied

The README documents roleFieldConfig for customizing the role field, and
src/index.ts passes it through:

const enhanceOptions = { ...options, roleFieldPlacement: ..., roleFieldConfig: collectionConfig.roleFieldConfig }
enhanceAdminCollection(processedCollection, enhanceOptions)

…but enhanceCollectionWithRole (src/utils/enhanceAdminCollection.ts) only reads
options.skipPermissionChecks, options.skipIfRoleExists, and
options.roleFieldPlacement. options.roleFieldConfig is never used, so any
customization passed through it (e.g. admin.components.Cell to fix Bug 1) is
silently ignored.

Repro

  1. Payload 3.84.x + gatekeeper 1.1.0, MongoDB.
  2. Enhance the users collection, assign any role to a user.
  3. Open the Users list view → the Role column shows <No Role> for all rows.
  4. Try to fix it via roleFieldConfig: { admin: { components: { Cell: ... } } }
    → no effect (Bug 2).

Suggested fixes

  • (A) Ship a default admin.components.Cell on the generated role field that
    reads the label from the already-populated object. Fixes Bug 1 out of the box
    without touching the load-bearing population.
  • (B) Actually apply options.roleFieldConfig in enhanceCollectionWithRole
    so users can override the field (incl. the Cell). Fixes Bug 2.

Happy to open a PR for (A), (B), or both — let me know which shape you'd prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions