zino-hofmann / graphql-flutter

A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package.
https://zino-hofmann.github.io/graphql-flutter
MIT License
3.25k stars 620 forks source link

[SocketClient] message stream encountered error: type '_OneByteString' is not a subtype of type 'Map<String, dynamic>' in type cast #1228

Closed davidpanic closed 2 years ago

davidpanic commented 2 years ago

Describe the issue

[SocketClient] message stream encountered error: type '_OneByteString' is not a subtype of type 'Map<String, dynamic>' in type cast

To Reproduce

To be honest I have no idea how to reproduce this, it just kind of randomly happens. I was hoping you could help me figure it out. To me it seems like it might be an issue on your side.

device / execution context

Seen in release builds on android 10, 11 and 12, Flutter Channel stable, 3.3.1, dart 2.18.0 (stable). I cannot reproduce it in debug mode.

Other useful/optional fields

Stacktrace: ```dart #0 GraphQLSocketMessage.parse (package:graphql/src/links/websocket_link/websocket_messages.dart:61) #1 _MapStream._handleData (dart:async/stream_pipe.dart:213) #2 _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153) #3 _rootRunUnary (dart:async/zone.dart:1399) #4 _CustomZone.runUnary (dart:async/zone.dart:1300) #5 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1209) #6 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339) #7 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271) #8 _SyncBroadcastStreamController._sendData. (dart:async/broadcast_stream_controller.dart:385) #9 _BroadcastStreamController._forEachListener (dart:async/broadcast_stream_controller.dart:322) #10 _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:384) #11 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:244) #12 _AsBroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:474) #13 _rootRunUnary (dart:async/zone.dart:1399) #14 _CustomZone.runUnary (dart:async/zone.dart:1300) #15 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1209) #16 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339) #17 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271) #18 _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123) #19 _HandleErrorStream._handleData (dart:async/stream_pipe.dart:253) #20 _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153) #21 _rootRunUnary (dart:async/zone.dart:1399) #22 _CustomZone.runUnary (dart:async/zone.dart:1300) #23 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1209) #24 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339) #25 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271) #26 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:774) #27 _StreamController._add (dart:async/stream_controller.dart:648) #28 _StreamController.add (dart:async/stream_controller.dart:596) #29 new _WebSocketImpl._fromSocket. (dart:_http/websocket_impl.dart:1144) #30 _rootRunUnary (dart:async/zone.dart:1399) #31 _CustomZone.runUnary (dart:async/zone.dart:1300) #32 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1209) #33 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339) #34 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271) #35 _SinkTransformerStreamSubscription._add (dart:async/stream_transformers.dart:63) #36 _EventSinkWrapper.add (dart:async/stream_transformers.dart:13) #37 _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:332) #38 _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:226) #39 _SinkTransformerStreamSubscription._handleData (dart:async/stream_transformers.dart:111) #40 _rootRunUnary (dart:async/zone.dart:1399) #41 _CustomZone.runUnary (dart:async/zone.dart:1300) #42 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1209) #43 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339) #44 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271) #45 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:774) #46 _StreamController._add (dart:async/stream_controller.dart:648) #47 _StreamController.add (dart:async/stream_controller.dart:596) #48 _Socket._onData (dart:io-patch/socket_patch.dart:2324) #49 _rootRunUnary (dart:async/zone.dart:1399) #50 _CustomZone.runUnary (dart:async/zone.dart:1300) #51 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1209) #52 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339) #53 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271) #54 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:774) #55 _StreamController._add (dart:async/stream_controller.dart:648) #56 _StreamController.add (dart:async/stream_controller.dart:596) #57 _RawSecureSocket._sendReadEvent (dart:io/secure_socket.dart:1107) #58 _rootRun (dart:async/zone.dart:1383) #59 _CustomZone.run (dart:async/zone.dart:1293) #60 _CustomZone.runGuarded (dart:async/zone.dart:1201) #61 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1241) #62 _rootRun (dart:async/zone.dart:1391) #63 _CustomZone.run (dart:async/zone.dart:1293) #64 _CustomZone.bindCallback. (dart:async/zone.dart:1225) #65 TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart:420) #66 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398) #67 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429) #68 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192) ```

The client is initialized like this:

    final Map<String, String> _defaultHeaders;

    late AuthLink authLink;
    late HttpLink httpLink;
    late WebSocketLink wsLink;
    late Link link;

    Future<String?> getToken() async => /* token logic */;

...

      HttpLink _getHttpLink(String url) => HttpLink(
        url,
        defaultHeaders: _defaultHeaders,
      );

      AuthLink get _authLink => AuthLink(
        getToken: () async {
          final token = await getToken();
          return token == null ? null : 'Bearer $token';
        },
      );

    authLink = _authLink;
    httpLink = _getHttpLink(url);

    wsLink = WebSocketLink(
      wsUrl,
      config: SocketClientConfig(
        delayBetweenReconnectionAttempts: Duration(seconds: 4),
        initialPayload: () async {
          final token = await getToken();
          final headers = <String, dynamic>{
            'headers': {
              if (token != null) 'Authorization': 'Bearer $token',
            },
          };
          return headers;
        },
        headers: _defaultHeaders,
      ),
    );

    link = Link.split(
      (request) => request.isSubscription,
      wsLink,
      authLink.concat(httpLink),
    );

    client = GraphQLClient(cache: GraphQLCache(store: InMemoryStore()), link: link);

Sentry reports the following flutter_context:

Variable Value
Current Lifecycle State paused
Default Route Name /
Has Render View true
Initial Lifecycle State AppLifecycleState.resumed
vincenzopalazzo commented 2 years ago

This looks like a protocol parsing error

https://github.com/zino-hofmann/graphql-flutter/blob/main/packages/graphql/lib/src/links/websocket_link/websocket_messages.dart#L56-L62

What server are you using?

davidpanic commented 2 years ago

Sorry for the late response, I was away from work, we are using hasura version 2.9.0 running in a container. Specifically, it is hasura/graphql-engine:v2.9.0@sha256:ddfd9630ca8d1f6414b74983993b732e83cb76f1c44b89aa1f63b44670a9c61e

orestesgaolin commented 2 years ago

We're having the same error and the stacktrace we capture is pretty much the same as the one above.

graphql 5.1.2-beta.4 hasura v2.11.2-cloud.1

vincenzopalazzo commented 2 years ago

We're having the same error and the stacktrace we capture is pretty much the same as the one above.

Thanks, are you able to reproduce it with our API https://api.chat.graphql-flutter.dev/graphql

levonmaa commented 2 years ago

Chiming in with a very similar case.

` [VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'String' is not a subtype of type 'Map<String, dynamic>' in type cast

0 GraphQLSocketMessage.parse

1 _MapStream._handleData (dart:async/stream_pipe.dart:213:31)

2 _RootZone.runUnaryGuarded (dart:async/zone.dart:1586:10)

3 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)

4 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)

5 _SyncBroadcastStreamController._sendData. (dart:async/broadcast_stream_controller.dart:385:20)

6 _BroadcastStreamController._forEachListener (dart:async/broadcast_stream_controller.dart:322:15)

7 _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:384:5)

8 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:244:5)

9 _AsBroadcastStreamController.add (dart:async/broad<…>

`

and with the error pointing to line websocket_messages.dart:L#56

final payload = (map['payload'] ?? <String, dynamic>{}) as Map<String, dynamic>;

My graphql version is 5.1.1 and using the Hasura cloud instance.

orestesgaolin commented 2 years ago

What I found is that the message causing the issue looks like that:

{"type":"connection_error","payload":"Could not verify JWT: JWTExpired"}

The payload is String whereas GraphQLSocketMessage.parse expects it to be Map:

final payload =
        (map['payload'] ?? <String, dynamic>{}) as Map<String, dynamic>;
vincenzopalazzo commented 2 years ago

I think this PR https://github.com/zino-hofmann/graphql-flutter/pull/1242 is the fix but it required some more love

vincenzopalazzo commented 2 years ago

Fixed in https://github.com/zino-hofmann/graphql-flutter/pull/1242