Repository: mirumee/ariadne

Issue:
GraphQLTransportWSHandler sends invalid errors message breaking `graphql-ws` clients on error (should be an array)
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",`).

```plain
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?
https://github.com/mirumee/ariadne/blob/3218cff61ce12dc649b1f425c26e23715c3241a1/ariadne/asgi/handlers/graphql_transport_ws.py#L318-L332

https://github.com/mirumee/ariadne/blob/3218cff61ce12dc649b1f425c26e23715c3241a1/ariadne/asgi/handlers/graphql_transport_ws.py#L368-L379


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

Resolving patch:
diff --git a/ariadne/asgi/handlers/graphql_transport_ws.py b/ariadne/asgi/handlers/graphql_transport_ws.py
--- a/ariadne/asgi/handlers/graphql_transport_ws.py
+++ b/ariadne/asgi/handlers/graphql_transport_ws.py
@@ -326,7 +326,7 @@ async def handle_websocket_subscribe(
                 {
                     "type": GraphQLTransportWSHandler.GQL_ERROR,
                     "id": operation_id,
-                    "payload": self.error_formatter(error, self.debug),
+                    "payload": [self.error_formatter(error, self.debug)],
                 }
             )
             return
@@ -375,7 +375,7 @@ async def get_results():
                 {
                     "type": GraphQLTransportWSHandler.GQL_ERROR,
                     "id": operation_id,
-                    "payload": results_producer[0],
+                    "payload": [results_producer[0]],
                 }
             )
         else:


Test patch:
diff --git a/tests/asgi/snapshots/snap_test_query_execution.py b/tests/asgi/snapshots/snap_test_query_execution.py
--- a/tests/asgi/snapshots/snap_test_query_execution.py
+++ b/tests/asgi/snapshots/snap_test_query_execution.py
@@ -73,15 +73,17 @@
     'message': "Cannot query field 'error' on type 'Subscription'."
 }
 
-snapshots['test_attempt_execute_subscription_with_invalid_query_returns_error_json_graphql_transport_ws 1'] = {
-    'locations': [
-        {
-            'column': 16,
-            'line': 1
-        }
-    ],
-    'message': "Cannot query field 'error' on type 'Subscription'."
-}
+snapshots['test_attempt_execute_subscription_with_invalid_query_returns_error_json_graphql_transport_ws 1'] = [
+    {
+        'locations': [
+            {
+                'column': 16,
+                'line': 1
+            }
+        ],
+        'message': "Cannot query field 'error' on type 'Subscription'."
+    }
+]
 
 snapshots['test_complex_query_is_executed_for_post_json_request 1'] = {
     'data': {
diff --git a/tests/asgi/test_websockets_graphql_transport_ws.py b/tests/asgi/test_websockets_graphql_transport_ws.py
--- a/tests/asgi/test_websockets_graphql_transport_ws.py
+++ b/tests/asgi/test_websockets_graphql_transport_ws.py
@@ -236,7 +236,7 @@ def test_custom_query_validator_is_used_for_subscription_over_websocket_transpor
         else:
             assert response["type"] == GraphQLTransportWSHandler.GQL_ERROR
             assert response["id"] == "test2"
-            assert response["payload"]["message"] == "Nope"
+            assert response["payload"][0]["message"] == "Nope"
 
 
 def test_custom_query_parser_is_used_for_query_over_websocket_transport_ws(