Skip to content

Postgres + versions: select hasMany nested 2+ array levels deep crashes save (text uuid inserted into integer parent_id) #17142

Description

@duvillierA

Describe the Bug

With a Postgres adapter and defaultIDType: 'number' (serial — the default), saving a versioned/draft document crashes when the document contains a select field with hasMany: true nested two or more array levels deep (arrayarrayselect hasMany).

The insert into the version table for the select values fails because Payload puts the parent array row's text uuid into the version child's parent_id column, which is typed integer:

error: invalid input syntax for type integer: "6a425fc0f04da02c1c6e5399"
  code:  22P02
  where: unnamed portal parameter $2 = '...'     // $2 = parent_id
  query: insert into "_things_v_version_outer_inner_days"
         ("order", "parent_id", "value", "id")
         values ($1, $2, $3, default), ...

The string 6a425fc0f04da02c1c6e5399 is the inner array row's id (Payload's text uuid for array rows). It is being inserted into parent_id, an integer column.

Depth matters (verified):

  • select hasMany at the document root → works (links to _things_v.id, an integer).
  • select hasMany inside a single array level → works (links to the array row's new serial version id).
  • select hasMany inside an array nested in another array (depth ≥ 2) → crashes.

Link to the code that reproduces this issue

Reproduced locally via the Local API. Minimal config:

// collection
{
  slug: 'things',
  versions: { drafts: true },
  fields: [
    {
      name: 'outer',
      type: 'array',
      fields: [
        {
          name: 'inner',
          type: 'array',
          fields: [
            {
              name: 'days',
              type: 'select',
              hasMany: true,
              options: ['monday', 'tuesday', 'wednesday'],
            },
          ],
        },
      ],
    },
  ],
}

// db: postgresAdapter / vercelPostgresAdapter, defaultIDType defaults to 'number' (serial)

Reproduction Steps

const doc = await payload.create({ collection: 'things', data: {} })

await payload.update({
  collection: 'things',
  id: doc.id,
  draft: true,
  data: { outer: [{ inner: [{ days: ['monday'] }] }] },
})
// throws: invalid input syntax for type integer: "<inner array row uuid>"

(Also reproduces through the admin UI: edit a drafts-enabled doc, add an outer row → an inner row → pick a value in the hasMany select → save/autosave.)

Expected vs Actual

  • Expected: the version snapshot inserts the select values, linking each child to its parent array row by the parent's new serial version-row id (as it already does correctly at depth 0 and depth 1).
  • Actual: at depth ≥ 2 the version select-table parent_id (typed integer) is populated with the parent array row's text uuid (the value stored in the parent version row's _uuid), producing invalid input syntax for type integer and aborting the transaction. No save succeeds while any such nested select value is set.

Root cause (analysis)

In Postgres version tables, array rows get a fresh serial id and keep the original text row id in _uuid. The version child table for a nested select hasMany is created with parent_id integer (FK → the parent array version row's serial id). When building the version row, Payload appears to use the parent array row's original text id (_uuid) as parent_id instead of the remapped serial id. This only surfaces when the parent array is itself nested inside another array — at shallower levels the parent id used is already an integer, so the type mismatch never appears.

Which area(s) are affected?

Database (@payloadcms/db-vercel-postgres / Postgres adapters), Versions / Drafts.

Environment

Payload 3.85.0
DB adapter @payloadcms/db-vercel-postgres 3.85.0 (drizzle-orm 0.45.2)
PostgreSQL 17
defaultIDType number (serial, default)
Node 24.15.0
Next.js 16.2.6
OS macOS

Workaround

Avoid select hasMany nested two array levels deep in a versioned collection. Store the multi-select as a json field (string[]), or use a sub-array of { value: select } instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions