Open alanrussian opened 5 years ago
I deployed my iPhone Flutter app two days ago and saw this exact same issue yesterday (out of about 10 users).
What is of note, is that this is a request which happened after the application was unpaused. (I suspend any attempts to get more data while the app is in the background and then resume when the app comes back).
I am seeing this as well. How do you guys handle this when it occurs? Catch the exception, recreate the http client and re-run the request(s)?
Thanks @alanrussian for that link. This is a terrifying error for people who know Unix syscalls and not that special behavior :).
It'd be great if Flutter on iOS could map this to a less scary error as my read of that link is that we should consider this like any other connection closed type event (except that this one happened due to the program's execution being suspended not the peer closing the connection).
HttpException: Bad file descriptor, uri = https://domain/i_18409528_1647229044.jpg
Empty stacktrace.
Any update on this?
up
uuuuuup
Same issue here:
SocketException: Bad file descriptor (OS Error: Bad file descriptor, errno = 9), address = firebasestorage.googleapis.com, port = 51490
Seems to be happening using the Image.network
in our case.
@eric-khoury Exactly the same for us. Seems to be on a failed NetworkImage.load in our case: (Reporting via Firebase Crashlytics)
Fatal Exception: FlutterError
0 ??? 0x0 _HttpClient.getUrl (dart:_http)
1 ??? 0x0 NetworkImage._loadAsync + 86 (_network_image_io.dart:86)
2 ??? 0x0 NetworkImage.load + 49 (_network_image_io.dart:49)
3 ??? 0x0 ImageProvider.resolveStreamForKey.<fn> + 488 (image_provider.dart:488)
4 ??? 0x0 ImageCache.putIfAbsent + 379 (image_cache.dart:379)
5 ??? 0x0 ImageProvider.resolveStreamForKey + 486 (image_provider.dart:486)
6 ??? 0x0 ScrollAwareImageProvider.resolveStreamForKey + 106 (scroll_aware_image_provider.dart:106)
7 ??? 0x0 ImageProvider.resolve.<fn> + 333 (image_provider.dart:333)
8 ??? 0x0 ImageProvider._createErrorHandlerAndKey.<fn> + 448 (image_provider.dart:448)
9 ??? 0x0 SynchronousFuture.then + 41 (synchronous_future.dart:41)
10 ??? 0x0 ImageProvider._createErrorHandlerAndKey + 445 (image_provider.dart:445)
11 ??? 0x0 ImageProvider.resolve + 330 (image_provider.dart:330)
12 ??? 0x0 _ImageState._resolveImage + 1119 (image.dart:1119)
13 ??? 0x0 _ImageState.didChangeDependencies + 1071 (image.dart:1071)
14 ??? 0x0 StatefulElement.performRebuild + 4974 (framework.dart:4974)
15 ??? 0x0 Element.rebuild + 4529 (framework.dart:4529)
16 ??? 0x0 BuildOwner.buildScope + 2659 (framework.dart:2659)
17 ??? 0x0 WidgetsBinding.drawFrame + 891 (binding.dart:891)
18 ??? 0x0 RendererBinding._handlePersistentFrameCallback + 370 (binding.dart:370)
19 ??? 0x0 SchedulerBinding._invokeFrameCallback + 1146 (binding.dart:1146)
20 ??? 0x0 SchedulerBinding.handleDrawFrame + 1083 (binding.dart:1083)
21 ??? 0x0 SchedulerBinding._handleDrawFrame + 997 (binding.dart:997)
This should be fixed because it happens to frequently on IOS devices.
I have this error a lot as well since all the Iphones did the last update, what can be done about it ?
Same here. Frequently happens on IOS devices
Facing this issue as well
Same here
iOS 16.1 Flutter 3.3.9
SocketException: Bad file descriptor (OS Error: Bad file descriptor, errno = 9)
while connecting to WS on GoLang
I am seeing the same issue on an iPhone X, running iOS 15.0. Any ideas on how to fix this?
Our new logger revealed a bit more detail on this, here is stacktrace:
0 IOClient.send (package:http/src/io_client.dart:88)
1 <asynchronous suspension>
2 BaseClient._sendUnstreamed (package:http/src/base_client.dart:93)
3 <asynchronous suspension>
4 _withClient (package:http/http.dart:164)
5 <asynchronous suspension>
I deployed my iPhone Flutter app two days ago and saw this exact same issue yesterday (out of about 10 users).
What is of note, is that this is a request which happened after the application was unpaused. (I suspend any attempts to get more data while the app is in the background and then resume when the app comes back).
Same issue here, were you able to solve this issue?
Same issue here!
same issue here.
same issue here
same issue here
Happening with me too when application goes in background and brought back in foreground after sometime, not sure if this is http or network?
why is this ticket still open and not assigned to anyone?
@dr0-dev Because it's normal for iOS to do this and there's nothing to suggest anything is wrong on the Flutter side, other than to think about overriding the OS error message to something more specific so people stop worrying about it?
Is there any update on this open issue please?
Hi,
As @alanrussian said, it is not unexpected for sockets to be invalidated when the app is suspended. From Apple's Documentation:
If you do leave your data socket open when going into the background, you must correctly handle errors on that socket. Handling errors is not a new requirement, but it is particularly important in this case because, if your app gets suspended, the socket's resources might get reclaimed by the kernel, after which all networking operations on the socket will fail. The only thing you can do with the socket at this point is to close it.
Note: When your app resumes execution the actual error returned by a socket's whose resources have been reclaimed is purposely not specified here to allow for future refinements. However, in many cases the error will be EBADF, which is probably not what you were expecting! Under normal circumstances EBADF means that the app has passed an invalid file descriptor to a system call. However, in the case of a socket whose resources have been reclaimed, it does not mean that the file descriptor was invalid, just that the socket is no longer usable.
Wrapping your Client
in RetryClient
should mask the symptoms i.e. RetryClient(client, whenError: (o, s) => o is SocketError)
Reducing idleTimeout
might reduce the frequency of this issue.
You could also consider using package:cupertino_http
(which is compatible with package:http
), where this issue will likely occur less because the OS manages the connection pool. But this failure can still happen:
If you're using NSURLConnection, the connection will call your -connection:didFailWithError: delegate method to signal the error.
I'm open to ideas on what else we can do e.g.
EBADF
isn't just for reclaimed socketsHttpClient
connection pool when the application is suspected - will reduce performance in some cases, would require coordination with flutterSomething else?
@natebosch Any ideas?
Someone on the Flutter team had a suggestion: we add a method to clear the connection pool on HttpClient and we ask developers to do that before their apps are suspended. So something like this:
HttpClient client;
...
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
client.clearConnectionPool(); // Think of a better name.
}
}
2. that's a bit dangerous - it might mask real errors because
EBADF
isn't just for reclaimed sockets
What other situations cause this error? If we could reliably detect the situation I'd be tempted to retry automatically, because I think that's the right solution for any usage scenario I can image.
If we can't reliably detect it, how would we frame the documentation - when would our users want to retry or not?
The situations that I can think of were this error might occur are:
HttpClient.connectionFactory
But, AFAIK, there is no canonical source for what failures can result in EBADF
- obviously Apple is using it for a scenario not described by the POSIX specification.
Someone on the Flutter team had a suggestion: we add a method to clear the connection pool on HttpClient and we ask developers to do that before their apps are suspended. So something like this:
HttpClient client; ... @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.paused) { client.clearConnectionPool(); // Think of a better name. } }
@brianquinlan By doing this it will not give bad file descriptor error but what to do with stucked api call? how it will re-call again and give response once un-lock device. thanks!
Is this ever going to be addressed
Is this ever going to be addressed
I'm not convinced that https://github.com/dart-lang/http/issues/197#issuecomment-1428812398 is the best solution:
dart:io
HttpClientUsing package:cupertino_http
should mitigate this issue.
But I'd love to get more feedback on possible approaches!
cupertino_http works for me! If you are using Dio i can also recommend the native_dio_adapter package.
up^
If you are using Dio i can also recommend the native_dio_adapter package.
Thanks @Surio89, that worked. No errors anymore.
Can confirm this is happening on http: ^1.2.0
@hls-app You are using IOClient
, which is not package:cupertino_http
.
Thanks @brianquinlan. As mentioned here https://api.flutter.dev/flutter/dart-io/HttpClient-class.html requires you to close the client. What would be the best practise here? Closing after every requests or closing when the app exits?
What would be the best practise here? Closing after every requests or closing when the app exits?
AFAIK, it's better to keep the client if you're planning to make requests to the same host later. In this case, in certain cases, the connection is kept alive, which decreases the amount of time needed for the next request.
Thanks @utopicnarwhal. In my case, they are mostly serverless API calls so I assume there is no added benefit with keeping the connection alive?
Also, do you have any examples on closing the client when the app is not in use? I was using http until now and all of these seems a bit of extra work in flutter world.
In my case, they are mostly serverless API calls so I assume there is no added benefit with keeping the connection alive?
Even though it's "serverless" there is still a server but not your own. So you may want to check the support of this feature on your backend. You can read more here
Also, do you have any examples on closing the client when the app is not in use?
No, I don't. I'am just creating one http client per API server in the initialisation stage of the app. And keeping them until the app is killed by the OS (whcih also frees all the resources).
@utopicnarwhal Thanks for the clarification. Does that mean, you don't manually close the client? I read about people complaining about potential leaks. So just want to make sure if this is an accepted approach.
Even though it's "serverless" there is still a server but not your own. So you may want to check the support of this feature on your backend. You can read more here
I am not sure if I agree with this. Lambdas for example are stateless so I am not sure if there are any benefits to keep the connections alive or if that matter anyway.
Does that mean, you don't manually close the client?
Yes, because I had no cases when I'm sure that certain http client isn't going to be used in the app anymore at all.
I read about people complaining about potential leaks
You may get the memory leak only if you create a new instance of the http client for every request without disposing the previous.
Even though it's "serverless" there is still a server but not your own
I am not sure if I agree with this Lambdas for example are stateless
Do you disagree that there is a computer instance (server) that processes your request and runs your code when you use Lambda? 🤔
The same issue was reproduced on Flutter latest version
flutter_error_exception
SocketException: Connection reset by peer (OS Error: Connection reset by peer, errno = 54), address = 192.168.10.34, port = 63558
-- | --
[✓] Flutter (Channel stable, 3.16.3, on macOS 14.2.1 23C71 darwin-arm64, locale en-IN)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)
[✓] VS Code (version 1.81.1)
[✓] Connected device (5 available)
For anyone experiencing this issue, I'd strongly suggest that you use package:cupertino_http
.
@brianquinlan this is still happening with package:cupertino_http.
ClientException with SocketException: Bad file descriptor (OSError: OS Error: Bad file descriptor, errno = 9)
PS: I am not closing the http client leaving it for the system to handle and using serverless API calls.
@hls-app Are you sure that you are using cupertino_http
in the configuration that is generating the exception?
Because only the IOClient
generates that exception
@brianquinlan yes, using cupertino_http
_ClientSocketException: ClientException with SocketException: Bad file descriptor (OS Error: Bad file descriptor, errno =...
HttpApiClient({Client? httpClient})
: _httpClient = httpClient ??
(Platform.isIOS
? CupertinoClient.defaultSessionConfiguration()
: CronetClient.defaultCronetEngine());
The Client
is from http
The URI link was a 404.
We deploy a Flutter app and have been noticing frequent exceptions from our HTTP requests:
From my discussion with @johnfesa, it is sometimes expected for iOS to throw these bad file descriptor errors. See Apple's Networking and Multitasking documentation. However, we're filing this issue for a few reasons:
Since this exception is expected to occur on iOS, we were wondering if it might be a good idea to raise a more specific exception than a generic OS error.
We're noticing this error frequently enough that we wanted to double check that there isn't an issue with the way the sockets are being managed that would be causing it.