Skip to content

Function-based admin.hidden invalidates existing Lexical block relationships #18121

Description

@jenslys

Describe the Bug

BlocksFeature applies applyBaseFilterToFields to relationship fields inside Lexical blocks. The injected filterOptions calls the target collection's admin.hidden function and returns false when that function returns true for the current user.

Payload's relationship validation interprets filterOptions: false as meaning that every selected ID is invalid. As a result, a user with read access to the target collection cannot save an otherwise valid document containing an already-selected relationship. Saving without changing the relationship fails with Invalid selections.

This couples admin UI visibility to persisted relationship validity. It is also inconsistent with static configuration: the injected filter checks function-based admin.hidden, but does not check admin.hidden: true.

The relevant path is:

  1. @payloadcms/richtext-lexical recursively wraps relationships inside BlocksFeature blocks.
  2. The wrapper returns false when admin.hidden({ user }) returns true.
  3. validateFilterOptions adds the relation to falseCollections and rejects the existing ID.

Expected behavior: hiding a collection from a user's admin UI should not invalidate an existing relationship that the same user is authorized to read. At minimum, saving an untouched relationship should not fail validation because of admin.hidden.

This behavior appears to originate from #14985, which added applyBaseFilterToFields for Lexical blocks. The same function-based hidden check also exists in the Lexical internal-link relationship filter, so that path may be affected as well.

Link to the code that reproduces this issue

https://github.com/jenslys/payload/tree/21b39fd4c0678873ea2d3ed91e09980ebfa42582/test/_community

The reproduction is based directly on the v3.87.0 tag.

Reproduction Steps

  1. Check out the reproduction commit:

    git clone --branch repro/lexical-admin-hidden-validation https://github.com/jenslys/payload.git
    cd payload
  2. Install dependencies and start the _community fixture:

    pnpm install
    pnpm exec cross-env PAYLOAD_DATABASE=sqlite pnpm dev _community
  3. Open the admin panel and use the configured auto-login user.

  4. Open Reproduction Posts and edit Saving this post fails. The seeded Lexical block already references Existing relationship target.

  5. Click Save without changing anything.

  6. The save fails with an Invalid selections validation error on relatedItem.

  7. Change hidden: ({ user }) => Boolean(user) to hidden: false, restart, and repeat. The same post can now be saved.

The logged-in user retains normal read access to hidden-items; only admin.hidden hides the collection.

The reproduction also contains an integration test expressing the expected behavior. It was run with:

pnpm exec cross-env PAYLOAD_DATABASE=sqlite pnpm test:int _community

On Payload 3.87.0 it fails with:

AssertionError: expected 400 to be 200
Content > Related Item: This field has the following invalid selections: 1

Which area(s) are affected?

  • plugin: richtext-lexical
  • area: core
  • area: ui

Environment Info

Payload: 3.87.0
@payloadcms/richtext-lexical: 3.87.0
Next.js: 16.3.0
React: 19.2.7
React DOM: 19.2.7
Node.js: 24.13.0
Package manager: pnpm (upstream reproduction), Bun 1.3.6 (original application)
Database: PostgreSQL in the original application; SQLite in the public reproduction
Operating system: macOS Darwin 25.5.0 arm64

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions