Skip to content

Null error locations cause serialisation exceptions #3885

Closed
@RafeArnold

Description

@RafeArnold

Describe the bug
When parsing an invalid GraphQL schema, it is possible that the SchemaProblem exception thrown by SchemaGenerator will contain errors that have null source locations. i.e. GraphQLError.getLocations() can return a list containing nulls. When these GraphQLErrors are serialised via GraphQLError.toSpecification(), an NPE is thrown due to GraphQLErrorHelper.location() expecting a non-null SourceLocation argument.

To Reproduce

import graphql.GraphQLError;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.errors.SchemaProblem;

String sdl = """
        extend schema
        @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
        
        type Query {
            user: User
        }
        
        type User @key(fields: "id") {
            id: ID!
        }
        """;
try {
    SchemaGenerator.createdMockedSchema(sdl);
} catch (SchemaProblem e) {
    e.getErrors().stream().map(GraphQLError::toSpecification).forEach(System.out::println);
}

The code inside the catch block will throw an NPE.

A test for the bug:

    def "toSpecification filters out null error locations"() {
        given:
        def error = ValidationError.newValidationError()
                .validationErrorType(ValidationErrorType.UnknownType)
                .sourceLocations([null, mkLocation(333, 1)])
                .description("Test ValidationError")
                .build()

        def expectedMap = [
                locations: [
                        [line: 333, column: 1]
                ],
                message: "Test ValidationError",
                extensions: [classification:"ValidationError"]
        ]

        expect:
        error.toSpecification() == expectedMap
    }

Potential solutions

A simple fix would be for BaseError.getLocations() to add an additional null check to node.getSourceLocation():

    @Override
    public List<SourceLocation> getLocations() {
        return node == null || node.getSourceLocation() == null ? Collections.singletonList(NO_WHERE) : Collections.singletonList(node.getSourceLocation());
    }

Or the potential of a nullable SourceLocation could be pushed further down into GraphQLErrorHelper:

    public static Object location(SourceLocation location) {
        if (location == null) {
            return null;
        }
        int line = location.getLine();
        int column = location.getColumn();
        if (line < 1 || column < 1) {
            return null;
        }
        return Map.of("line", line, "column", column);
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions