Skip to content

Sparse blocks array persisted with gapped _order (db-postgres) — undefined block row bricks the entire edit view #17508

Description

@jravas

Describe the Bug

A block add through the admin UI persisted a sparse blocks array to Postgres: the surviving rows kept their original array indexes (_order 1 and 3) with no row at _order 2. On every subsequent read, Payload rehydrates the gap as an undefined array entry, and the form-state builder throws:

Block with type "undefined" was found in block data, but no block with that type is defined in the config for field with schema path promotionListings.layout.

The result is that the entire edit view for the document fails to load in the affected locale — editors are locked out of the document until the underlying rows are repaired (a draft save that happens to rewrite the array also clears it).

This is the same end-state reported in #10485 (closed as unreproducible, now locked — the bot's closing message says to open a new issue if it persists). We hit it on payload 3.84.1, i.e. long after the form-state overhaul (#11946) that was believed to make it unreproducible, and we have DB forensics narrowing down when and how the bad data was written.

Forensic evidence

Collection: drafts enabled (localizeStatus: true, schedulePublish: true, maxPerDoc: 50), a localized: true blocks field named layout with 4 block types, @payloadcms/db-postgres.

Version-table trace for the affected document (version_updated_by_id joined, block rows unioned per version, one locale shown):

Version Saved (UTC) Action layout rows (type:_order)
v4/v5 2026-07-22 08:50 publish (locale A) header:1
v6/v7 2026-07-22 08:52 publish (locale A) header:1, cardGrid:3gap born here
v8/v9 2026-07-23 12:28 publish (locale B) header:1, cardGrid:3 (inherited)

Between two saves 2 minutes apart, a block add went from 1 block to rows at positions 1 and 3 — the middle row was silently dropped at write time while its neighbour kept index 3. This was not a reorder/delete; the same editing session also produced a This field requires at least 1 Rows. validation error on one of the block types (an array field with minRows: 1), which may be the row that vanished. Only one locale of the document was affected; the corruption then propagated into every subsequent version snapshot, and into the published tables on publish.

Two independent problems

  1. Write path: the postgres adapter accepts a sparse/invalid rows array and persists non-contiguous _order values instead of rejecting the write or compacting the array. Whatever produces the undefined middle row client-side, the server persisting it turns a UI glitch into durable data corruption.
  2. Read path: addFieldStatePromise throws on an undefined block row, which fails form-state for the whole document. A degraded render (skip the row / show an error placeholder, as requested in Block Undefined in Array of Blocks and Unable to Load Edit View for Document #10485) would keep the document editable so editors could fix it themselves.

We'd be happy to contribute a PR for (2) if maintainers agree with the skip-with-warning approach.

Link to the code that reproduces this issue

https://github.com/infonomic/payload-block-error

Reproduction Steps

Note on the reproduction link: it is the repro repo from #10485 (by @58bits), which reproduces the same failure class. We cannot yet reproduce the client-side trigger on demand — matching #10485 — but the corrupt state and the resulting lock-out reproduce deterministically:

  1. Create a blank app (pnpx create-payload-app@latest -t blank) with @payloadcms/db-postgres, drafts enabled, and a blocks field (e.g. layout) with 2+ block types. Localization is how we hit it in production but is not required for the read-path failure.
  2. Create a document with 3 blocks in layout and publish/save it.
  3. Simulate the sparse write our production trace shows (a middle row dropped at write time while the neighbour keeps its index) by removing the middle block row directly in the DB, leaving _order 1 and 3 — e.g. DELETE FROM <collection>_blocks_<middle_block> WHERE _parent_id = <doc_id> AND _order = 2; (do the same in the _<collection>_v_blocks_* version tables for the latest draft version).
  4. Open the document in the admin panel.
  5. Observed: the edit view fails to load; the server logs Block with type "undefined" was found in block data, but no block with that type is defined in the config for field with schema path <collection>.layout on every form-state request. The document is uneditable.
  6. Expected: the write path never persists a gapped _order sequence (reject or compact), and/or the form-state builder degrades gracefully (skip the undefined row with a warning) so the document stays editable.

Which area(s) are affected?

db: postgres, area: ui

Environment Info

Production (where the corruption was written): Payload 3.84.1, Next.js 16.2.9, Node.js 22 (Docker on Kubernetes), Postgres 18 (AWS RDS), multiple locales, localized blocks field, drafts with localizeStatus.

`pnpm payload info` from the affected project:

Relevant Packages:
  payload: 3.84.1
  next: 16.2.9
  @payloadcms/db-postgres: 3.84.1
  @payloadcms/live-preview-react: 3.84.1
  @payloadcms/next/utilities: 3.84.1
  @payloadcms/plugin-form-builder: 3.84.1
  @payloadcms/plugin-import-export: 3.84.1
  @payloadcms/plugin-mcp: 3.84.1
  @payloadcms/plugin-nested-docs: 3.84.1
  @payloadcms/plugin-redirects: 3.84.1
  @payloadcms/plugin-search: 3.84.1
  @payloadcms/plugin-seo: 3.84.1
  @payloadcms/richtext-lexical: 3.84.1
  @payloadcms/storage-s3: 3.84.1
  @payloadcms/ui/shared: 3.84.1
  react: 19.2.4
  react-dom: 19.2.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bugarea: uiRelated to the admin panel.db: postgres@payloadcms/db-postgresstatus: needs-triagePossible bug which hasn't been reproduced yetv3

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions