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:
codecov[bot]: ## [Codecov](https://codecov.io/gh/mirumee/ariadne/pull/1071?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) Report
> Merging [#1071](https://codecov.io/gh/mirumee/ariadne/pull/1071?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) (523f2c0) into [master](https://codecov.io/gh/mirumee/ariadne/commit/81a77136b4a28c6ec0da2462e3207c8a4804847d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) (81a7713) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #1071   +/-   ##
=======================================
  Coverage   98.11%   98.11%           
=======================================
  Files         115      115           
  Lines        7463     7463           
=======================================
  Hits         7322     7322           
  Misses        141      141           
```


| [Impacted Files](https://codecov.io/gh/mirumee/ariadne/pull/1071?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) | Coverage Δ | |
|---|---|---|
| [ariadne/asgi/handlers/graphql\_transport\_ws.py](https://codecov.io/gh/mirumee/ariadne/pull/1071?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee#diff-YXJpYWRuZS9hc2dpL2hhbmRsZXJzL2dyYXBocWxfdHJhbnNwb3J0X3dzLnB5) | `99.47% <ø> (ø)` | |
| [tests/asgi/snapshots/snap\_test\_query\_execution.py](https://codecov.io/gh/mirumee/ariadne/pull/1071?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee#diff-dGVzdHMvYXNnaS9zbmFwc2hvdHMvc25hcF90ZXN0X3F1ZXJ5X2V4ZWN1dGlvbi5weQ==) | `100.00% <100.00%> (ø)` | |
| [tests/asgi/test\_websockets\_graphql\_transport\_ws.py](https://codecov.io/gh/mirumee/ariadne/pull/1071?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee#diff-dGVzdHMvYXNnaS90ZXN0X3dlYnNvY2tldHNfZ3JhcGhxbF90cmFuc3BvcnRfd3MucHk=) | `100.00% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee)


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa6ed03..6b8d5bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,13 @@
 # CHANGELOG
 
 ## 0.20 (UNRELEASED)
+
 - Added `query_validator` option to ASGI and WSGI `GraphQL` applications that enables customization of query validation step.
+- Fixed `ERROR` message in GraphQL-WS protocol having invalid payload type.
 
 
 ## 0.19.1 (2023-03-28)
+
 - Fixed `.graphql` definitions files not being included in the dist files
 
 
diff --git a/ariadne/asgi/handlers/graphql_transport_ws.py b/ariadne/asgi/handlers/graphql_transport_ws.py
index 4f05238..26c48c9 100644
--- a/ariadne/asgi/handlers/graphql_transport_ws.py
+++ b/ariadne/asgi/handlers/graphql_transport_ws.py
@@ -326,7 +326,7 @@ class GraphQLTransportWSHandler(GraphQLWebsocketHandler):
                 {
                     "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 @@ class GraphQLTransportWSHandler(GraphQLWebsocketHandler):
                 {
                     "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
index 341310b..855b9de 100644
--- a/tests/asgi/snapshots/snap_test_query_execution.py
+++ b/tests/asgi/snapshots/snap_test_query_execution.py
@@ -73,15 +73,17 @@ snapshots['test_attempt_execute_subscription_with_invalid_query_returns_error_js
     '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
index 0a10f8b..649070c 100644
--- 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(