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)
Nobody has claimed this yet.
- 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
- 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, } - 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. ✅ - Remove one item via the field's ✕ (2 remain).
- Open Choose from existing again, tick 1 more, press Select 1.
- ❌ 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). - ❌ 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
minRowsvalidation on a block sub-field prevents publish
(or at minimum surfaces the error), matchingmaxRowsbehaviour.
Environment
- Payload: 3.86.0 (
@payloadcms/richtext-lexical3.86.0,@payloadcms/ui3.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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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