mirumee / ariadne

Python library for implementing GraphQL servers using schema-first approach.

Home Page:https://ariadnegraphql.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

GraphQLTransportWSHandler sends invalid errors message breaking `graphql-ws` clients on error (should be an array)

kevinvalk opened this issue · comments

Lately I switched over to fully using the GraphQLTransportWSHandler for everything (including mutations and queries). However, I am receiving the following error on the client ("graphql-ws": "^5.11.2",).

index.ts:43 Error: "error" message expects the 'payload' property to be an array of GraphQL errors, but got {"message":".......","locations":[{"line":1,"column":36}],"extensions":{"exception":null}}
    at validateMessage (common.mjs:128:23)
    at parseMessage (common.mjs:168:12)
    at socket2.onmessage (client.mjs:202:37)

After checking graphql-ws source code and protocol specification I think Apollo is not sending errors correctly:
https://github.com/enisdenjo/graphql-ws/blob/fbb763a662802a6a2584b0cbeb9cf1bde38158e0/src/utils.ts#L57-L66
https://github.com/enisdenjo/graphql-ws/blob/fbb763a662802a6a2584b0cbeb9cf1bde38158e0/PROTOCOL.md#error

The client expects an array of errors, but instead Apollo only sends a single error?

try:
query_document = parse_query(context_value, self.query_parser, data)
operation_type = get_operation_type(
query_document, data.get("operationName")
)
except GraphQLError as error:
log_error(error, self.logger)
await websocket.send_json(
{
"type": GraphQLTransportWSHandler.GQL_ERROR,
"id": operation_id,
"payload": self.error_formatter(error, self.debug),
}
)
return

# if success then AsyncGenerator is expected, for error it will be List
results_producer = get_results() if success else [result]
if not success:
results_producer = cast(List[dict], results_producer)
await websocket.send_json(
{
"type": GraphQLTransportWSHandler.GQL_ERROR,
"id": operation_id,
"payload": results_producer[0],
}
)

I'll mark this as a bug and put this on my radar for next release.

I only got to test this just now, but @rafalp the fix is not completely according spec and will still throw errors:

GraphQL websocket error:  Error: "error" message expects the 'payload' property to be an array of GraphQL errors, but got [{"errors":[{"message":"Unknown argument 'id' on field 'Mutation.test'.","locations":[{"line":2,"column":20}],"extensions":{"exception":null}}]}]
{
    "type": "error",
    "id": "c243e373-4048-4108-a8d5-d5a23ed8e796",
    "payload": [
        {
            "errors": [
                {
                    "message": "Unknown argument 'id' on field 'Mutation.test'.",
                    "locations": [
                        {
                            "line": 2,
                            "column": 20
                        }
                    ],
                    "extensions": {
                        "exception": null
                    }
                }
            ]
        }
    ]
}

The spec mentions that when type == 'error' the payload is directly a list of errors, while currently it is a list of objects that contains the "errors" key with a list of errors.

import { GraphQLError } from 'graphql';

interface ErrorMessage {
  id: '<unique-operation-id>';
  type: 'error';
  payload: GraphQLError[];
}

Reopening this

If I understand the spec right, if there's an error, instead of NEXT we should send ERROR message, because this protocol is not supporting partialy correct results?

It seems to me that there are three places in GraphQLTransportWSHandler where errors can be returned to the client, and neither of those can create the error response you are mentioning:

await websocket.send_json(

type is ERROR and payload is list of error objects, not {"errors": [...]}. Looks okay.

await websocket.send_json(

type is ERROR and payload is a list of result responses. Looks like this is the culprit. But I'll need further study.

Third place is where NEXT is returned with payload.errors instead of data:

I can reproduce this. This error only occurs for query and mutation operations sent to GraphQL Transport WS.

I've released 0.22 beta 1 with the fix.