Patch-release GraphQL-core v3.2.14, based on GraphQL.js v16.15.0.
This patch-release supports Python 3.7 to 3.15.
This release contains security fixes. It fixes four denial-of-service vulnerabilities in the validator and the parser, so upgrading is recommended for all users of GraphQL-core 3.2. It also includes behavior changes, new features and bug fixes, see below. The security fixes are also available for GraphQL-core 3.3 in version 3.3.2.
Security fixes:
MaxIntrospectionDepthRulewalked fragments repeatedly, so validating a small document with the default rules could take exponential time. Fragments already checked at the same or a greater depth are now skipped. The rule also no longer raisesRecursionErrorfor long fragment chains under an introspection field.- Fragments shared by many operations were walked again for every operation, so validation with the default rules took quadratic time. Validation now also stops with "Validation aborted." once the work on shared fragments exceeds 50,000 units (module attribute
graphql.validation.validation_context.MAX_FRAGMENT_WORK). - The budget of
OverlappingFieldsCanBeMergedRulefrom CVE-2026-75507 counted only field comparisons, so some documents could still take quadratic time. It now covers all of the rule's work (see below). max_tokensfrom CVE-2026-75508 was only checked after a whole run of comments had been read. It is now enforced while the comments are skipped.
Behavior changes:
- Documents, fragment chains and variable values nested deeper than 200 levels are now rejected with a GraphQL error instead of raising
RecursionErrorin the request path (#216). GraphQL.js has no such limit. Parsing gives "Syntax Error: Document is nested too deeply. Parsing aborted.", validation gives "Validation aborted.", variable values give "Variable values are nested too deeply." and execution without validation gives "Selections are nested too deeply. Execution aborted." Methods ofParsercalled directly and utilities such asseparate_operationscan still raiseRecursionError, and inside of fields it stays a field error. - The limit also helps on PyPy, where exhausting the stack can crash the interpreter. This is a PyPy issue that newer PyPy versions do not fix yet. We recommend that users on PyPy upgrade GraphQL-core. Deep recursion elsewhere, for instance in your own code, can still run into the PyPy issue.
- When the budget of
OverlappingFieldsCanBeMergedRuleis exhausted, the whole validation now stops with the errors found so far and "Validation aborted.", instead of only this rule stopping. The limit is nowgraphql.validation.rules.overlapping_fields_can_be_merged.MAX_COMPLEXITY_BUDGET, 500,000 units by default, and also counts comparisons of return type wrappers.MAX_FIELD_COMPARISONSis deprecated. If it is changed, the budget is two times its value and aDeprecationWarningis issued. - Conflict messages of this rule are now bounded. Descriptions are abbreviated after 1000 characters, and at most 20 locations are kept per side.
ValidationAbortedErroris now a subclass ofGraphQLErrorinstead ofRuntimeError, andvalidate()returns it as the last error when it aborts, also for the error limit.- A
GraphQLErrorcreated while an exception is handled no longer adopts the traceback of that exception. When raised, it gets its own traceback and the handled exception as its context. - Non-integer numbers such as
Decimal("2.5")now give a field error when serialized asIntinstead of being truncated, as in GraphQL.js. Objects that are not numbers but can be converted to an int still work. - Only CRLF, LF and CR now count as line breaks in source locations and block strings, as in GraphQL.js (#294). This changes the
locationsof errors at a line start, which reported the previous line.
New features:
- New option
hide_suggestionsofgraphql,validate,execute,subscribeand related functions, as in version 3.3. It removes "Did you mean" from validation and input coercion errors. - New parser options
max_lexical_tokens,max_commentsandmax_source_lengthfor all parse functions andbuild_schema.max_lexical_tokensworks as in GraphQL.js and does not count comments, so combine it with one of the others.max_source_lengthis measured in code points, where GraphQL.js uses UTF-16 code units. max_tokensis deprecated in favor of these options, without a warning. Unlike in GraphQL.js it is not an alias: it keeps counting comments, so existing code keeps its protection.build_schemanow accepts it too.- New option
max_indent_depthofprint_ast. From that depth on, nothing is indented and no newlines are inserted, while block strings keep theirs. Error messages now use at most 100 levels of indentation.
Bug fixes:
str(error)no longer raisesIndexErrorfor sources containing characters like VT, FF, NEL or U+2028, andprint_astformats block strings with these characters or trailing newlines like GraphQL.js (#295).- Conflicts between equal selection sets of fragments on different types were missed in documents parsed with
no_location=True. - The default type resolver no longer leaves coroutines of
is_type_ofunawaited when a lateris_type_ofraises. MapAsyncIterator.athrowkeeps the original error if closing a source withoutathrowalso fails.
Performance:
- Printing is 57 to 62 percent faster on CPython on normal documents and 22 to 42 percent on PyPy.
- Fields repeated in a selection set are validated faster, 69 ms instead of 102 ms for 1000 of them on CPython, and errors with many locations no longer scan the source for each of them.
Thanks to Nikhil Agrawal, Mettle Labs (@nikhilagrawalsf) and zx (@manus-use), who independently reported the problem with the introspection depth, to zx also for the reports on shared fragments and the budget, to @eliteaja11-dev for a variant of the latter, and to Zachariah Mithani (AploSec, @zachariah-mithani) for the report on comments. Thanks also to @devkral for reporting the issue with deep nesting, and to @arxhr007 for reporting and fixing the issue with line breaks.
Thanks to @jkimbo for sponsoring this project.