Skip to content

Bug: clipboard copy/paste creates phantom out-of-bounds rows when array has 10+ siblings #16741

Description

@dawndarkness

Describe the Bug

Copy Row → Paste Row on a Payload array (or block) field creates phantom rows the editor cannot delete when the array has 10 or more sibling rows AND the source row's index is a digit prefix of another row's index (e.g. row 1 vs rows 10-19, row 2 vs rows 20-29).

After paste, empty rows appear at out-of-bounds indices like .50, .51, .52. The field then fails validation and blocks save. The phantom rows cannot be removed via the UI.

A second variant of the same bug affects textual field-name prefixes (e.g. children vs childrenOther) in the fromRowToField cleanup branch.

Root cause

packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.ts filters form-state by key.startsWith(prefix) at three callsites without enforcing a path boundary:

  1. reduceFormStateByPath's filter loop — builds the clipboard payload, so the leak originates here.
  2. mergeFormStateFromClipboard's paste loop predicate — rewrites leaked keys to out-of-bounds paths instead of dropping them.
  3. mergeFormStateFromClipboard's fromRowToField cleanup branch — can delete unrelated fields whose name shares a textual prefix.

'children.1'.startsWith('children.1') matches 'children.10', 'children.11', etc. 'children'.startsWith('children')matches 'childrenOther'.

Reproduces on @payloadcms/ui@3.84.1 and on current main (v4). PR with fix + four unit-level regression tests will be linked.

Link to the code that reproduces this issue

https://github.com/dawndarkness/payload/blob/fix/clipboard-prefix-collision/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts

Reproduction Steps

Unit-level reproduction (deterministic, no app needed):

  1. Check out https://github.com/payloadcms/payload at main.
  2. Replace
    packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts
    with the version from the linked branch (adds a new describe('prefix collision with multi-digit sibling indices') block; existing tests
    untouched).
  3. Run pnpm install && pnpm vitest run --project unit packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts
  4. Observe: 4 new tests fail, 9 existing tests pass.

End-user UI reproduction:

  1. Collection with an array field permitting at least 13 rows.
  2. Populate 13 rows with distinctive titles.
  3. Row Copy on row 1 (zero-indexed).
  4. Paste into any other row.
  5. Phantom empty rows appear at out-of-bounds positions; field fails validation.

Which area(s) are affected?

area: ui

Environment Info

Payload version: 3.84.1 (bug also reproduces on `main` / v4)
@payloadcms/ui: 3.84.1
Node.js version: 24.15.0
Next.js version: 16.x
Database adapter: any (UI-only bug, DB-agnostic)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: uiRelated to the admin panel.v3

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions