API Platform version(s) affected: 4.3.17 (also reproduces on 4.3.4)
Description
A custom #[GraphQl\Query]'s args: entry that names a GraphQL enum type by string — one that only exists in the schema because another resource has a property typed with that backed enum — crashes the entire schema build with:
The type "WidgetStatus!" was not resolved.
thrown from TypeConverter::resolveType(). The root cause: typesContainer only gets WidgetStatus registered as a side effect of building Widget's own GraphQL fields, which has not necessarily happened yet when FieldsBuilder::resolveResourceArgs() eagerly resolves this custom query's arg type. Because schema build is a single pass over the whole Query type, this takes down every /graphql request, not just the one query that declared the bad arg — there is no partial-schema fallback.
This looks like the same root cause as #8068 ("Type ... is not present in the types container", nested mutation input, still open) and #3651 (a custom ObjectType referencing an entity type too early, closed wontfix) — different trigger, same underlying typesContainer-populated-lazily-and-incompletely problem.
How to reproduce
Minimal Symfony 7 + api-platform/symfony + api-platform/graphql project, no Doctrine, no database. Two plain resources:
src/Enum/WidgetStatus.php:
enum WidgetStatus: string
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
}
src/ApiResource/Widget.php — property typed with the enum, which is what registers the WidgetStatus GraphQL type:
#[ApiResource(
operations: [],
graphQlOperations: [new Query(resolver: WidgetQueryResolver::class)],
)]
final class Widget
{
#[ApiProperty(identifier: true)]
public string $id = '1';
public WidgetStatus $status = WidgetStatus::DRAFT;
}
src/ApiResource/WidgetStatusOptions.php — an unrelated resource whose custom query's arg names that enum:
#[ApiResource(
operations: [],
graphQlOperations: [
new Query(
name: 'widgetStatusOptions',
resolver: WidgetStatusOptionsQueryResolver::class,
args: ['status' => ['type' => 'WidgetStatus!']],
read: false,
),
],
)]
final class WidgetStatusOptions
{
#[ApiProperty(identifier: true)]
public string $id = '1';
/** @var list<string> */
public array $allowedNext = [];
}
(Resolvers just return new Widget() / new WidgetStatusOptions() — happy to push the full minimal project to a repo on request.)
Running bin/console api:graphql:export (or hitting /api/graphql) throws immediately:
In TypeConverter.php line 134:
[ApiPlatform\Metadata\Exception\InvalidArgumentException]
The type "WidgetStatus!" was not resolved.
Exception trace:
TypeConverter->resolveType() at vendor/api-platform/graphql/Type/TypeConverter.php:134
FieldsBuilder->resolveResourceArgs() at vendor/api-platform/graphql/Type/FieldsBuilder.php:307
FieldsBuilder->getItemQueryFields() at vendor/api-platform/graphql/Type/FieldsBuilder.php:91
SchemaBuilder->getSchema() at vendor/api-platform/graphql/Type/SchemaBuilder.php:63
GraphQlExportCommand->execute() at vendor/api-platform/symfony/Bundle/Command/GraphQlExportCommand.php:69
Possible Solution
Either resolve the type regardless of resource-processing order (e.g. force-build every resource's fields, or at least every resource that contributes a type referenced from args:, before resolving custom-query arg types), or fail at attribute-declaration time with a clear message saying a custom query's args: cannot reference a resource-derived type — rather than crashing the whole schema at request time with a message that doesn't say why the type wasn't found.
Workaround we're using in the meantime: declare the arg as 'type' => 'String!' and match the incoming value against WidgetStatus::cases() by hand inside the resolver. This is presumably why every other custom-query-with-a-typed-arg example in the docs/tests only uses String/Int/Float.
Additional Context
API Platform version(s) affected: 4.3.17 (also reproduces on 4.3.4)
Description
A custom
#[GraphQl\Query]'sargs:entry that names a GraphQL enum type by string — one that only exists in the schema because another resource has a property typed with that backed enum — crashes the entire schema build with:thrown from
TypeConverter::resolveType(). The root cause:typesContaineronly getsWidgetStatusregistered as a side effect of buildingWidget's own GraphQL fields, which has not necessarily happened yet whenFieldsBuilder::resolveResourceArgs()eagerly resolves this custom query's arg type. Because schema build is a single pass over the wholeQuerytype, this takes down every/graphqlrequest, not just the one query that declared the bad arg — there is no partial-schema fallback.This looks like the same root cause as #8068 ("Type ... is not present in the types container", nested mutation input, still open) and #3651 (a custom
ObjectTypereferencing an entity type too early, closed wontfix) — different trigger, same underlyingtypesContainer-populated-lazily-and-incompletely problem.How to reproduce
Minimal Symfony 7 +
api-platform/symfony+api-platform/graphqlproject, no Doctrine, no database. Two plain resources:src/Enum/WidgetStatus.php:src/ApiResource/Widget.php— property typed with the enum, which is what registers theWidgetStatusGraphQL type:#[ApiResource( operations: [], graphQlOperations: [new Query(resolver: WidgetQueryResolver::class)], )] final class Widget { #[ApiProperty(identifier: true)] public string $id = '1'; public WidgetStatus $status = WidgetStatus::DRAFT; }src/ApiResource/WidgetStatusOptions.php— an unrelated resource whose custom query's arg names that enum:#[ApiResource( operations: [], graphQlOperations: [ new Query( name: 'widgetStatusOptions', resolver: WidgetStatusOptionsQueryResolver::class, args: ['status' => ['type' => 'WidgetStatus!']], read: false, ), ], )] final class WidgetStatusOptions { #[ApiProperty(identifier: true)] public string $id = '1'; /** @var list<string> */ public array $allowedNext = []; }(Resolvers just return
new Widget()/new WidgetStatusOptions()— happy to push the full minimal project to a repo on request.)Running
bin/console api:graphql:export(or hitting/api/graphql) throws immediately:Possible Solution
Either resolve the type regardless of resource-processing order (e.g. force-build every resource's fields, or at least every resource that contributes a type referenced from
args:, before resolving custom-query arg types), or fail at attribute-declaration time with a clear message saying a custom query'sargs:cannot reference a resource-derived type — rather than crashing the whole schema at request time with a message that doesn't say why the type wasn't found.Workaround we're using in the meantime: declare the arg as
'type' => 'String!'and match the incoming value againstWidgetStatus::cases()by hand inside the resolver. This is presumably why every other custom-query-with-a-typed-arg example in the docs/tests only usesString/Int/Float.Additional Context
typesContainer-populated-lazily root cause, different trigger.