payloadcms / payloadcms/payload

Upload field values inside Lexical block forms are replaced, not appended, when the list drawer cycles (minRows error also does not block publish)

Open
#17,731 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug plugin: richtext-lexical
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

Describe the Bug

Inside a Lexical rich-text block's edit form, an upload field with
hasMany: true loses its existing value whenever the "Choose from
existing" list drawer is used a second time. The drawer's bulk-select
("Select N") is documented/implemented as append (onChange([...existing, ...new]) in @payloadcms/ui fields/Upload/Input.js), but inside a
Lexical block form the field's current value is stale (empty) by the time
the handler runs — so append behaves as replace, deterministically.

The same root cause also affects multiple sibling single upload
fields
in one Lexical block: pick a photo in field 1, open field 2's
drawer, and field 1's value is gone (unless a draft was saved in between).

Secondary finding: the block sub-field's minRows validation computes
but does not gate publish
. After the replacement left 1 item in a
minRows: 2 field, the form-state response contained
"errorMessage": "1 is less than the min allowed Rows of 2.", "valid": false
— yet the document published without complaint. (maxRows DOES appear to
be enforced in the picker; minRows is silently ignored at publish.)

Reproduced with Payload's stock admin UI only — no custom components
(a custom beforeListTable component was explicitly disabled to isolate).

To Reproduce

  1. A collection with a Lexical rich-text field whose BlocksFeature
    includes a block containing:
    {
      name: 'photos',
      type: 'upload',
      relationTo: 'media',
      hasMany: true,
      required: true,
      minRows: 2,
      maxRows: 3,
    }
    
  2. In a post, add the block. Open Choose from existing, tick 3 media
    rows, press Select 3 → 3 items land correctly. Save draft —
    database shows all 3 stored. ✅
  3. Remove one item via the field's ✕ (2 remain).
  4. Open Choose from existing again, tick 1 more, press Select 1.
  5. ❌ The field now contains ONLY the new item. The form-state response
    for the block shows "photos": { "value": [<newId>] } — the 2 existing
    items were already absent from the submitted state (they are not
    dropped server-side; the client-side append started from empty).
  6. ❌ Publish succeeds despite the form state carrying the minRows
    validation error above.

Variant: give the block three separate single upload fields instead —
selecting into field 2 wipes field 1 unless a draft is saved in between.

Expected behaviour

  • Bulk/single selection from the list drawer appends to the field's
    current value inside Lexical block forms, as it does for the same field
    outside Lexical.
  • A failing minRows validation on a block sub-field prevents publish
    (or at minimum surfaces the error), matching maxRows behaviour.

Environment

  • Payload: 3.86.0 (@payloadcms/richtext-lexical 3.86.0, @payloadcms/ui 3.86.0)
  • Database: PostgreSQL (@payloadcms/db-postgres, Neon)
  • Next.js: 16.x, App Router
  • Browser: Chrome (macOS)

Notes

  • Deterministic, not a race: every drawer round-trip replaces.
  • Closest existing issues reviewed and ruled out: #13643 (autosave
    validation errors — fixed in 3.55), #8500 (drawer navigation —
    fixed in beta), #10555 (InlineBlock persistence — fixed).

Contributor guide

Open the contributing guide

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.

Research direction

Start with @payloadcms/ui fields/Upload/Input.js and the Lexical block form state handling described in the report. Reproduce the provided hasMany upload flow and the sibling-field variant, then trace how minRows validation reaches publish. Done means existing selections append in Lexical blocks and a failing minRows validation prevents or surfaces publish failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
nextjs, postgresql, react, typescript
Domain
backend, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.