Skip to content

GraphQL schema creation fails with duplicate type name for recursive depth-limited block structures #17302

Description

@alphanull

Describe the Bug

Using the documented community pattern for recursive block structures — a depth-limited factory that returns one block config object per nesting level, all sharing the same slug and interfaceName (see e.g. discussion #4238) — breaks GraphQL schema creation entirely:

Error: Schema must contain uniquely named types but contains multiple types named "RecursiveItemBlock".

Every request against /api/graphql then returns a 500, including a trivial { __typename } query. TypeScript type generation handles the same pattern fine (deduplicates into interfaces), and the REST/Local APIs work normally — only the GraphQL schema build fails.

Root cause: the blocks handler in packages/graphql/src/schema/fieldToSchemaMap.ts deduplicates block types via the graphqlResult.types.blockTypes registry (keyed by block slug), but it only registers the newly built type after calling objectType.getFields(). That call eagerly resolves the type's fields, which walks nested blocks fields and re-enters the handler while the registry entry is still missing — so each nesting level constructs its own GraphQLObjectType with the same name, and new GraphQLSchema(...) rejects the schema.

Affects both v3 (reproduced on 3.85.1 and 3.86.0) and current main.

Fix PR (register the type before forcing field resolution, plus regression tests): #17300

Link to the code that reproduces this issue

https://github.com/alphanull/payload/tree/repro/graphql-recursive-blocks

(follows the reproduction guide: single commit adding a recursive block field to test/_community's Posts collection)

Reproduction Steps

  1. Check out the reproduction branch and run pnpm install.
  2. Run pnpm dev:generate-graphql-schema _community (or start pnpm dev _community and POST {"query":"{ __typename }"} to /api/graphql).
  3. Schema creation throws Schema must contain uniquely named types but contains multiple types named "RecursiveItemBlock".

The added config is just:

const buildRecursiveBlock = (depth: number): Block => {
  const fields: Field[] = [{ name: 'label', type: 'text', required: true }]

  if (depth > 1) {
    fields.push({
      name: 'children',
      type: 'blocks',
      blocks: [buildRecursiveBlock(depth - 1)],
    })
  }

  return {
    slug: 'recursiveItem',
    fields,
    interfaceName: 'RecursiveItemBlock',
  }
}

// in a collection:
{
  name: 'recursiveBlockField',
  type: 'blocks',
  blocks: [buildRecursiveBlock(3)],
}

Which area(s) are affected?

area: graphql

Environment Info

Binaries:
  Node: 24.18.0
  npm: 11.16.0
  Yarn: N/A
  pnpm: 11.5.2
Relevant Packages:
  payload: 3.86.0
Operating System:
  Platform: linux
  Arch: arm64
  Version: #1 SMP Thu Jun 25 13:45:40 UTC 2026
  Available memory (MB): 7937
  Available CPU cores: 4

Also reproduced against current main (4.0.0-beta.0) with Node 24.18.0.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    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