Skip to content

Field error from list arg with nullable variable entry (nullable=optional clash?) #1002

Open
@benjie

Description

@benjie

Consider the following currently valid schema and query:

type Query {
  sum(numbers:[Int!]!): Int
}

query Q ($number: Int = 3) {
  sum(numbers: [1, $number, 3])
}

With the following variables:

{
  "number": null
}

Now following through 6.1.2 Coercing Variable Values:

  1. Let coercedValues be an empty unordered Map. ✅
  2. Let variablesDefinition be the variables defined by operation. ✅
  3. For each variableDefinition in variablesDefinition: ✅
    1. Let variableName be the name of variableDefinition. ✅
    2. Let variableType be the expected type of variableDefinition. ✅
    3. Assert: IsInputType(variableType) must be true. ✅
    4. Let defaultValue be the default value for variableDefinition. ✅
    5. Let hasValue be true if variableValues provides a value for the name variableName. ✅
    6. Let value be the value provided in variableValues for the name variableName. ✅
    7. If hasValue is not true and defaultValue exists (including null):
      1. Add an entry to coercedValues named variableName with the value defaultValue.
    8. Otherwise if variableType is a Non-Nullable type, and either hasValue is not true or value is null, raise a request error.
    9. Otherwise if hasValue is true: ✅
      1. If value is null: ✅
        1. Add an entry to coercedValues named variableName with the value null. ✅

For the variable definition $number: Int = 3:

  • variableType is Int (NOT Int!).
  • defaultValue is 3
  • hasValue is true since number is provided in variables (even though it's null)
  • value is null

Thus coercedValues becomes { number: null }.

When it comes to executing the field, this results in CoerceArgumentValues() raising a field error at 5.j.iii.1 (since [1, null , 3] cannot be coerced to [Int!]!).

Had the query have been defined with ($number: Int! = 3) then this error could not have occurred; but we explicitly allow the nullable Int with default in IsVariableUsageAllowed(). Presumably this is so that we can allow a default value to apply in the list, except this is a ListValue so there cannot be a default value there.

We've discussed "optionality" versus "nullability" a few times, but I'd like to get the WG's read on this.

Reproduction:

const { graphqlSync, GraphQLSchema, GraphQLList, GraphQLNonNull, GraphQLInt, GraphQLObjectType, validateSchema } = require('graphql');

const Query = new GraphQLObjectType({
  name: 'Query',
  fields: {
    sum: {
      type: GraphQLInt,
      args: {
        numbers: {
          type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLInt))),
        }
      },
      resolve(_, args) {
        let total = 0;
        for (const n of args.numbers) {
          total += n;
        }
        return total;
      }
    }
  }
});

const schema = new GraphQLSchema({
  query: Query
});

const errors = validateSchema(schema);
if (errors.length > 0) {
  throw errors[0];
}

{
  console.log('Testing query with variables and null');
  const result = graphqlSync({
    schema,
    source: `query Q($number: Int = 3) {sum(numbers:[1,$number,3])}`,
    variableValues: {
      number: null
    }
  });
  console.dir(result);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions