Description
When executing a query with multiple fields, if an async field's resolver is processed first (its inner resolve() coroutine gets stashed for later), and a later sibling NonNull field raises synchronously, the exception escapes execute_fields immediately, before the stashed coroutine is ever awaited or closed. This results in:
RuntimeWarning: coroutine 'ExecutionContext.execute_fields.<locals>.resolve' was never awaited
Environment
- graphql-core: 3.3.0a11
- strawberry-graphql: 0.291.3 (via experimental_execute_incrementally)
- Python: 3.12.13
Minimal reproduction
from __future__ import annotations
import asyncio
import sys
from graphql import (
GraphQLField,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
parse,
)
from graphql.execution import experimental_execute_incrementally
async def resolve_slow_async(_obj, _info):
await asyncio.sleep(0.01)
return "ok"
def resolve_sync_raises_nonnull(_obj, _info):
raise Exception("boom from sibling")
schema = GraphQLSchema(
query=GraphQLObjectType(
name="Query",
fields={
"slow": GraphQLField(GraphQLString, resolve=resolve_slow_async),
"fast_fail": GraphQLField(
GraphQLNonNull(GraphQLString),
resolve=resolve_sync_raises_nonnull,
),
},
)
)
async def run():
try:
result = experimental_execute_incrementally(
schema=schema,
document=parse("{ slow fast_fail }"),
)
if asyncio.iscoroutine(result):
result = await result
print(f"result: {result!r}")
except Exception as exc:
print(f"raised: {type(exc).__name__}: {exc}")
if __name__ == "__main__":
sys.exit(asyncio.run(run()))
Observed output
result: ExecutionResult(data=None, errors=[GraphQLError('boom from sibling', ...)])
sys:1: RuntimeWarning: coroutine 'resolve_slow_async' was never awaited
sys:1: RuntimeWarning: coroutine 'ExecutionContext.complete_awaitable_value' was never awaited
sys:1: RuntimeWarning: coroutine 'ExecutionContext.execute_fields.<locals>.resolve' was never awaited
Description
When executing a query with multiple fields, if an async field's resolver is processed first (its inner
resolve()coroutine gets stashed for later), and a later siblingNonNullfield raises synchronously, the exception escapesexecute_fieldsimmediately, before the stashed coroutine is ever awaited or closed. This results in:Environment
Minimal reproduction
Observed output