Skip to content

fix(ui): deleted array item reappears after reorder with autosave#15906

Merged
PatrikKozak merged 8 commits intopayloadcms:mainfrom
ed-cscosta:fix/array-ghost-item-autosave-reorder
Mar 12, 2026
Merged

fix(ui): deleted array item reappears after reorder with autosave#15906
PatrikKozak merged 8 commits intopayloadcms:mainfrom
ed-cscosta:fix/array-ghost-item-autosave-reorder

Conversation

@ed-cscosta
Copy link
Contributor

@ed-cscosta ed-cscosta commented Mar 11, 2026

Overview

Fixes deleted array items reappearing after reordering with autosave enabled.

Key Changes

  • Added row ID validation in mergeServerFormState before accepting server values for array fields

    • Parses field paths to find the array path and row index (handles nested arrays)
    • Compares row IDs at the given index between client and server state
    • Rejects server value if IDs don't match or row was deleted client-side
  • Syncs the value field to match actual row count after merging when they differ

Design Decisions

When rows are reordered or deleted while an autosave request is in-flight, the same index can point to different rows in client vs server state. For example, after reordering [A, B, C] → [C, A, B] and deleting A, the client has [C, B] but the server responds with [C, A, B]. Without validation, array.1.text would get A's stale data instead of B's current data.

The fix parses paths right-to-left to find the deepest numeric index, ensuring nested arrays are handled correctly (e.g., blocks.0.items.1.text checks blocks.0.items[1], not blocks[0]).

During autosave with overrideLocalChanges: false, the client is source of truth for structural changes made while the request was pending.

Fixes #15889

Copy link
Contributor

@PatrikKozak PatrikKozak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ed-cscosta, thanks for the PR!

I found an edge case with nested arrays that might be an issue. When checking a path like blocks.0.items.1.text, your implementation stops at the first matching array (blocks) and doesn't verify the inner array (blocks.0.items).

This means if the inner array is reordered but the outer one isn't, stale data gets accepted.

I've added a couple test cases that covers this scenario and should fail with your current approach. The fix needs to check the deepest array level in the path rather than the first match.

I can go ahead and implement this in your PR

@ed-cscosta
Copy link
Contributor Author

Both CI failures are unrelated to this PR:

  • build-template-with-postgres: Docker network timeout pulling the postgres image from ghcr.io
  • E2E plugin-import-export: flaky test looking for a per-page button, unrelated to form state
    changes

…es to prevent stale data from overwriting the wrong row during autosave merges
@PatrikKozak PatrikKozak changed the title fix(ui): array ghost item after reorder and delete with autosave fix(ui): deleted array item reappears after reorder with autosave Mar 11, 2026
@PatrikKozak
Copy link
Contributor

PatrikKozak commented Mar 11, 2026

@ed-cscosta I reviewed your approach and came up with a bit of a simpler implementation that parses array paths on-demand instead of pre-computing Maps. It achieves the same result but with a bit less overhead.

Really appreciate your work on this!

PatrikKozak
PatrikKozak previously approved these changes Mar 11, 2026
@PatrikKozak PatrikKozak dismissed their stale review March 11, 2026 21:00

Adding e2e tests

@PatrikKozak PatrikKozak merged commit 752c15a into payloadcms:main Mar 12, 2026
150 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Array field: Reordering items followed by deletion creates a ghost item

3 participants