Fix "Cannot traverse an already closed generator" in Schema::getTypeMap()#1903
Merged
spawnia merged 1 commit intowebonyx:masterfrom Apr 21, 2026
Conversation
…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.
3c9407c to
76c2aa7
Compare
ruudk
approved these changes
Apr 21, 2026
Collaborator
ruudk
left a comment
There was a problem hiding this comment.
Good catch. Thanks for the PR.
Collaborator
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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::$typesis configured with a 'bare'Generator(an iterable that can only be traversed once), bothgetScalarOverrides()andgetTypeMap()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 bygetScalarOverrides()(either called fromgetType()beforehand, or from withingetTypeMap()itself), the subsequentforeachingetTypeMap()would fail with:"Cannot traverse an already closed generator".The fix
This fix extracts a
materializeTypes()method that bothgetScalarOverrides()andgetTypeMap()now call. This method resolvesconfig->typesby invoking the callable (if applicable) and converting any non-array iterable (such as aGenerator) into an array viaiterator_to_array(), storing the result back inconfig->types. This ensures the generator is consumed exactly once and all subsequent access operates on the materialized array.Changes
Schema::materializeTypes()that resolves callables, materializes generators to arrays, and caches the result inconfig->typesgetScalarOverrides()andgetTypeMap()both callmaterializeTypes()instead of duplicating the resolution logic