Skip to content

Fix "Cannot traverse an already closed generator" in Schema::getTypeMap()#1903

Merged
spawnia merged 1 commit intowebonyx:masterfrom
jerowork:jerowork/fix/types-overwrite-with-non-callable-generator
Apr 21, 2026
Merged

Fix "Cannot traverse an already closed generator" in Schema::getTypeMap()#1903
spawnia merged 1 commit intowebonyx:masterfrom
jerowork:jerowork/fix/types-overwrite-with-non-callable-generator

Conversation

@jerowork
Copy link
Copy Markdown
Contributor

Description

Fixing bug introduced at #1886, merged in version 15.31.5.

https://github.com/jerowork/graphql-attribute-schema/blob/main/src/SchemaBuilder.php#L56 is failing now since the SchemaBuilder uses a bare Generator as types input (which is allowed according to the private property type).

The problem

When SchemaConfig::$types is configured with a 'bare' Generator (an iterable that can only be traversed once), both getScalarOverrides() and getTypeMap() need to iterate over it.
Previously, each method independently resolved config->types; calling the callable if needed and then iterating the result. This caused a problem: if the generator was consumed by getScalarOverrides() (either called from getType() beforehand, or from within getTypeMap() itself), the subsequent foreach in getTypeMap() would fail with: "Cannot traverse an already closed generator".

The fix

This fix extracts a materializeTypes() method that both getScalarOverrides() and getTypeMap() now call. This method resolves config->types by invoking the callable (if applicable) and converting any non-array iterable (such as a Generator) into an array via iterator_to_array(), storing the result back in config->types. This ensures the generator is consumed exactly once and all subsequent access operates on the materialized array.

Changes

  • Added Schema::materializeTypes() that resolves callables, materializes generators to arrays, and caches the result in config->types
  • getScalarOverrides() and getTypeMap() both call materializeTypes() instead of duplicating the resolution logic

…ap()

When `SchemaConfig::$types` is configured with a 'bare' `Generator` (an
iterable that can only be traversed once, which is allowed according to
the private property type), both `getScalarOverrides()`
and `getTypeMap()` need to iterate over it. Previously, each method
independently resolved `config->types` — calling the callable if needed
and then iterating the result. This caused a problem: if the generator
was consumed by `getScalarOverrides()` (either called from `getType()`
beforehand, or from within `getTypeMap()` itself), the subsequent
`foreach` in `getTypeMap()` would fail with "Cannot traverse an already
closed generator".

This fix extracts a `materializeTypes()` method that both
`getScalarOverrides()` and `getTypeMap()` now call. This method resolves
`config->types` by invoking the callable (if applicable) and converting
any non-array iterable (such as a `Generator`) into an array via
`iterator_to_array()`, storing the result back in `config->types`. This
ensures the generator is consumed exactly once and all subsequent access
operates on the materialized array.
@jerowork jerowork force-pushed the jerowork/fix/types-overwrite-with-non-callable-generator branch from 3c9407c to 76c2aa7 Compare April 20, 2026 17:05
Copy link
Copy Markdown
Collaborator

@ruudk ruudk left a comment

Choose a reason for hiding this comment

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

Good catch. Thanks for the PR.

@ruudk ruudk requested a review from spawnia April 21, 2026 06:56
@spawnia spawnia merged commit 58a765d into webonyx:master Apr 21, 2026
22 checks passed
@spawnia
Copy link
Copy Markdown
Collaborator

spawnia commented Apr 21, 2026

Thanks @jerowork! Released as v15.32.1.

@jerowork
Copy link
Copy Markdown
Contributor Author

Thanks @jerowork! Released as v15.32.1.

Thanks for the quick review and release 🎉

@jerowork jerowork deleted the jerowork/fix/types-overwrite-with-non-callable-generator branch April 21, 2026 16:57
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.

3 participants