GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — reject null variable value for @skip and @include if argument by nab-iha3 · Pull Request #4540 · graphql-java/graphql-java

Conversation

@nab-iha3

@nab-iha3 nab-iha3 commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

spock, SkipAndInclude, query($flag: Boolean = true) { field @skip(if: $flag) } executed with {"flag": null}:

java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "java.util.Map.get(Object)" is null
    at graphql.execution.conditional.ConditionalNodes.getIfValue

the @skip/@include if argument is Boolean!, but a nullable variable with a non-null default value is a legal usage in that non-null position (the spec's IsVariableUsageAllowed), so the query passes validation. when the client then sends an explicit null, the coerced value is null and getIfValue does (boolean) variables.get(name), so the unboxing throws an uncaught NPE straight out of execution. the identical query written with $flag: Boolean! already returns a proper coercion error, and so does a null variable in a non-null list item or input field; the directive if argument is the one non-null position that was missed. the same NPE fires for @include, for nested fields, and from ExecutableNormalizedOperationFactory.

getIfValue now raises an AbortExecutionException (surfaced as a normal query error with data: null) when the resolved variable value is null for the non-null if argument, so the response carries an error instead of a thrown exception. the without-variables normalization path (variables == null) still returns null to defer the decision, so valid inputs, including the applied-default case, are unchanged.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@nab-iha3
reject null variable value for @Skip and @include if argument
06a0410
@github-actions

github-actions Bot commented Nov 6, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Test Report

Test Results

Java Version Total Passed Failed Errors Skipped
Java 17 6278 (+16 🟢) 6222 (+16 🟢) 0 (±0) 0 (±0) 56 (±0)
Java 21 6278 (+16 🟢) 6222 (+16 🟢) 0 (±0) 0 (±0) 56 (±0)
Java 25 6278 (+16 🟢) 6222 (+16 🟢) 0 (±0) 0 (±0) 56 (±0)
jcstress 32 (±0) 32 (±0) 0 (±0) 0 (±0) 0 (±0)
Total 18866 (+48 🟢) 18698 (+48 🟢) 0 (±0) 0 (±0) 168 (±0)

Code Coverage (Java 25)

Metric Covered Missed Coverage vs Master
Lines 30384 3078 90.8% ±0.0%
Branches 8975 1505 85.6% ±0.0%
Methods 8104 1186 87.2% ±0.0%

Changed Class Coverage (4 classes)

Class Line Branch Method
g.e.c.ConditionalNodes +0.9% 🟢 +1.3% 🟢 ±0.0%
g.e.Execution +0.3% 🟢 ±0.0% ±0.0%
g.e.ExecutionStrategy +0.1% 🟢 ±0.0% ±0.0%
g.e.NonNullableValueCoercedAsNullException +9.0% 🟢 +50.0% 🟢 +10.9% 🟢

Full HTML report: build artifact jacoco-html-report

Updated: 2026-11-06 14:18:20 UTC

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@andimarek

andimarek commented Nov 6, 2026

Copy link
Copy Markdown
Member

Reviewed 06a04107a6015dc96c4cf0c282031faed9631129, including its interaction with the already-merged #4522. The null check is needed, but I would request changes to the error handling.

1. [P1] Nested directive failures abort the whole operation.

The new AbortExecutionException discards successful sibling results. With a nullable parent field and variables {"flag": null}:

query($flag: Boolean = true) {
  ok
  parent { child @skip(if: $flag) }
}

I verified that both ok and parent execute, but the response contains only errors, classified as ExecutionAborted; data is absent. The nested failure should instead null the enclosing nullable parent, preserve the successful ok result, and report the error path. The same issue occurs with @include, asynchronous parent fetchers, mutations, and list selections. See the spec's execution-error handling.

Subscription handling also remains incomplete. For:

subscription($flag: Boolean = true) {
  event { child @skip(if: $flag) }
}

with the same variables, a source publisher containing two events emits no GraphQL results: the subscriber receives onError(AbortExecutionException) and the stream terminates. I reproduced this for both directives.

2. [P2] Preserve the source location and execution path.

The message-only exception has neither locations nor path, although the offending variable-reference AST node is available. Preserve its source location and attach the execution path when handling a nested failure. The spec recommends locations for identifiable document positions and requires paths for errors associated with a result field.

Relationship to #4522: that PR forces field argument coercion before invoking a data fetcher, within the existing field-error handling. It preserves sibling data and null propagation. This PR addresses directive arguments during field collection, so the changes complement each other; #4522 does not cover this path. The remaining work is to establish consistent coercion-error handling at the field-collection boundaries, rather than simply substituting another exception class.

Coverage: the two added tests cover only root fields. Their result.data == null assertion also accepts both absent data and an explicit "data": null. Please add nested nullable/non-null parents, partial results, paths/locations, and subscription-event cases. I ran 62 existing targeted cases successfully and 49 additional probes covering synchronous/asynchronous execution, mutations, lists, defaults, normalization, and subscriptions. The probes confirm the behavior described above; no production changes were made during the review.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@andimarek andimarek mentioned this pull request Nov 6, 2026
Use shared coercion and located errors for @skip and @include #4541
Open
@nab-iha3
report null @skip/@include if variables as field errors with location…
bbeed5b
… and path
@nab-iha3

nab-iha3 commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

agreed on both, reworked in bbeed5b.

getIfValue now throws NonNullableValueCoercedAsNullException carrying the location of the offending variable reference instead of AbortExecutionException. the two field-collection boundaries handle it:

  • nested (ExecutionStrategy.completeValue, around completeValueForObject): it's treated as a field error on the field whose sub-selection failed. the error gets that field's path plus the variable's location, and the field completes as null through the normal non-null validator, so a nullable parent is nulled, a non-null one propagates, and siblings are kept. that covers async parents, mutations and list items since they all go through completeValue, and subscription events too, so each event now emits {event: null} with the error and the stream keeps going.
  • root (Execution.executeOperation, around the root collectFields): there's no enclosing field to null, so it returns the error with no data entry, same as the $flag: Boolean! variable coercion failure.

ExecutableNormalizedOperationFactory and the QueryTraverser path now throw the same typed exception instead of the NPE.

tests in SkipAndInclude now cover, for both directives: root (asserts data absent from the spec map, location, no path), nested nullable parent with ok preserved for query and mutation, async parent, non-null parent propagating to the nullable grandparent with path [wrapper, inner], list items with paths [items, 0] / [items, 1], a two-event subscription with no onError, and the normalized operation. the 16 new cases fail on the previous production code; execution/normalized/analysis suites pass.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@nab-iha3 @andimarek
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.