DinoLeung / TeleDart

A Dart library interfacing with the latest Telegram Bot API.
https://pub.dev/packages/teledart
GNU General Public License v3.0
314 stars 70 forks source link

Unhandled LongPollingException #69

Closed putraxor closed 4 years ago

putraxor commented 5 years ago

Describe the bug

Unhandled exception after a few hours LongPollingException: HttpClientException: SocketException: Failed host lookup: 'api.telegram.org' (OS Error: nodename nor servname provided, or not known, errno = 8)

Logs

Unhandled exception:
LongPollingException: HttpClientException: SocketException: Failed host lookup: 'api.telegram.org' (OS Error: nodename nor servname provided, or not known, errno = 8)
#0      LongPolling._recursivePolling.<anonymous closure> (package:teledart/src/teledart/fetch/long_polling.dart:97:21)
#1      _rootRunUnary (dart:async/zone.dart:1132:38)
#2      _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#3      _FutureListener.handleError (dart:async/future_impl.dart:144:20)
#4      Future._propagateToListeners.handleError (dart:async/future_impl.dart:651:47)
#5      Future._propagateToListeners (dart:async/future_impl.dart:672:24)
#6      Future._completeError (dart:async/future_impl.dart:491:5)
#7      _SyncCompleter._completeError (dart:async/future_impl.dart:55:12)
#8      _Completer.completeError (dart:async/future_impl.dart:27:5)
#9      _AsyncAwaitCompleter.completeError (dart:async-patch/async_patch.dart:40:18)
#10     Telegram.getUpdates (package:teledart/src/telegram/telegram.dart)
DinoLeung commented 5 years ago

Thanks for reporting @putraxor . I am not 100% sure what had gone wrong, it looks to me you host machine went offline after a few hours.

ja2375 commented 4 years ago

I think this should not be closed. I'm getting this error too and i'm using teledart in an intranet application.

This error in fact happens if server looses internet connection. But i think crashing the whole app just because the server lost connectivity is not the best approach. What happens if the server running an app that uses teledart looses internet connection because of a carrier outage but the app is used inside the company's network (not on the internet!)? Is there anything i can do to prevent the whole app crash (maybe some error handling)?

Thanks in advance!

DinoLeung commented 4 years ago

@ja2375 i understand that crashing the application might not be the best, however due to long poll is basically making get request to the server in a interval, the best we can do is to swallow the error and keep retrying but then developer might miss errors cause by servier configuration. It is recommanded to use webhooks instead of long poll, as it is a passive approch rather than making get requests actively.

ja2375 commented 4 years ago

I'm testing to handle the exception by running all the application inside a runZonedGuarded(), which allows to handle all unhandled exceptions and preventing the app from crashing.

For the time being, i did not get any LongPollingException. I will keep testing because i want to know what happens if i catch the LongPollingException in the runZonedGuarded's onError.

ja2375 commented 4 years ago

Yesterday i got a LongPollingException at some point. But i checked and the application not only did not crash but also it was just working fine (including the telegram bot) when the machine recovered from the internet outage.

So i'm guessing that this is the correct way (and also the only one i found) that allows handling this exception. Here is exactly how you can handle it, but in fact there is no need to do anything when a LongPollingException is thrown as teledart will continue to work fine.

void main() =>
    runZonedGuarded<void>(() {
      // Your main function here
    }, (e, s) {
      switch(e.runtimeType) {
        case TeleDartException:
          if(e.toString().contains('LongPollingException')) {
            /// You can handle here the LPE...
            /// But it is not really needed as teledart
            /// handles it by itself and keeps working
            /// despite of throwing LPE.
          }
      }
      print('An error occurred: $e');
    });
shinayser commented 4 years ago
            /// You can handle here the LPE...
            /// But it is not really needed as teledart
            /// handles it by itself and keeps working
            /// despite of throwing LPE.

I am looking at the longPooling code and seems that when an error occurs, the long pooling stops it's recursive call, take a look:

 /// Private long polling loop, throws [LongPollingException] on error.
  void _recursivePolling() {
    if (_isPolling) {
      telegram
          .getUpdates(
              offset: offset,
              limit: limit,
              timeout: timeout,
              allowed_updates: allowed_updates)
          .then((updates) {
        if (updates.isNotEmpty) {
          for (var update in updates) {
            emitUpdate(update);
            offset = update.update_id + 1;
          }
        }
        _recursivePolling();
      }).catchError((error) =>
              // TODO: find out what exceptions can be ignored
              error is io.HandshakeException
                  ? _recursivePolling()
                  : throw LongPollingException(error.toString())); //The recursive call stops here
    }
  }

Maybe I am wrong, can you confirm that? Those Unhandled erros are driving me crazy too =/

DinoLeung commented 4 years ago

seems

@shinayser Yes, you are correct. Currently it only ignore io.HandshakeException. I am interested to know what execreption you are getting.

@ja2375 the code snippet you post bascally is jsut ignoring all TeleDartExceptions, which I am not too sure if it is a good idea.

I am looking into enhancing the long poll method right now, my idea is to halt when receiving client errors (e.g. 401 unauthorised), keep in the _recursivePolling loop otherwise and perhaps log the error somewhere, most likely just a console log.

DinoLeung commented 4 years ago

@shinayser @ja2375 give #118 a try, and let me know if what you think

shinayser commented 4 years ago

Hey @DinoLeung I will test it when I get home! My error case is really simple to replicate if you want to try: just start the long pooling bot, then cut off internet connection. It will crash.

ja2375 commented 4 years ago

@DinoLeung I'm getting the following exception after upgrading to 0.0.49, and i think it can be related to this issue. 3 seconds after starting the app, it throws:

Invalid argument (onError): Error handler must accept one Object or one Object and a StackTrace as arguments, and return a a valid result: Closure: (HttpClientException) => void from Function '_onRecursivePollingHttpError@196175636':.
#0      _registerErrorHandler (dart:async/future_impl.dart:828:3)
#1      Future.catchError (dart:async/future_impl.dart:308:17)
#2      LongPolling._recursivePolling (package:teledart/src/teledart/fetch/long_polling.dart:98:12)
#3      LongPolling._recursivePolling.<anonymous closure> (package:teledart/src/teledart/fetch/long_polling.dart:96:13)
#4      _rootRunUnary (dart:async/zone.dart:1198:47)
#5      _CustomZone.runUnary (dart:async/zone.dart:1100:19)
#6      _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
#7      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
#8      Future._propagateToListeners (dart:async/future_impl.dart:725:32)
#9      Future._completeWithValue (dart:async/future_impl.dart:529:5)
#10     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
#11     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
#12     Telegram.getUpdates (package:teledart/src/telegram/telegram.dart)
#13     _rootRunUnary (dart:async/zone.dart:1198:47)
#14     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
#15     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
#16     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
#17     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
#18     Future._completeWithValue (dart:async/future_impl.dart:529:5)
#19     Future.timeout.<anonymous closure> (dart:async/future_impl.dart:797:16)
#20     _rootRunUnary (dart:async/zone.dart:1198:47)
#21     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
#22     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
#23     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
#24     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
#25     Future._completeWithValue (dart:async/future_impl.dart:529:5)
#26     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
#27     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
#28     _withClient (package:http/http.dart)
#29     _rootRunUnary (dart:async/zone.dart:1198:47)
#30     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
#31     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
#32     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
#33     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
#34     Future._completeWithValue (dart:async/future_impl.dart:529:5)
#35     _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:40:15)
#36     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:311:13)
#37     Response.fromStream (package:http/src/response.dart)
#38     _rootRunUnary (dart:async/zone.dart:1198:47)
#39     _CustomZone.runUnary (dart:async/zone.dart:1100:19)
#40     _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
#41     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696:45)
#42     Future._propagateToListeners (dart:async/future_impl.dart:725:32)
#43     Future._completeWithValue (dart:async/future_impl.dart:529:5)
#44     Future._asyncCompleteWithValue.<anonymous closure> (dart:async/future_impl.dart:567:7)
#45     _rootRun (dart:async/zone.dart:1190:13)
#46     _CustomZone.run (dart:async/zone.dart:1093:19)
#47     _CustomZone.runGuarded (dart:async/zone.dart:997:7)
#48     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1037:23)
#49     _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#50     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
#51     _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:118:13)
#52     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:404:11)
#53     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428:5)
#54     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

I didn't get this exception on 0.0.47, so it should be a recent addition.

DinoLeung commented 4 years ago

@ja2375 whoops, just fixed this. An update will be up soonish