Skip to content

Frequent bad file descriptor errors on iOS #197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
alanrussian opened this issue Sep 10, 2018 · 75 comments
Open

Frequent bad file descriptor errors on iOS #197

alanrussian opened this issue Sep 10, 2018 · 75 comments

Comments

@alanrussian
Copy link

We deploy a Flutter app and have been noticing frequent exceptions from our HTTP requests:

SocketException: OS Error: Bad file descriptor, errno = 9, address = <redacted domain>, port = 64436

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.

@mulderpf
Copy link

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).

@kuhnroyal
Copy link

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)?

@willbryant
Copy link

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).

@dmitry-fbm
Copy link

dmitry-fbm commented Mar 22, 2022

HttpException: Bad file descriptor, uri = https://domain/i_18409528_1647229044.jpg

Empty stacktrace.

@kdcyberdude
Copy link

Any update on this?

@nobrefelipe
Copy link

up

@DjordjeMancic97
Copy link

uuuuuup

@eric-khoury
Copy link

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.

@html-rulez-d00d
Copy link

@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)

@fkranenburg
Copy link

This should be fixed because it happens to frequently on IOS devices.

@GaelleJoubert
Copy link

I have this error a lot as well since all the Iphones did the last update, what can be done about it ?

@KalinRangelovRangelov
Copy link

Same here. Frequently happens on IOS devices

@thankiyash
Copy link

Facing this issue as well

@WiRight
Copy link

WiRight commented Dec 5, 2022

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

@MartinCastellon
Copy link

MartinCastellon commented Dec 12, 2022

I am seeing the same issue on an iPhone X, running iOS 15.0. Any ideas on how to fix this?

@dmitry-fbm
Copy link

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>

@ArkeshGKalathiya
Copy link

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?

@tulioccalazans
Copy link

Same issue here!

@sikandernoori
Copy link

same issue here.

@Surio89
Copy link

Surio89 commented Jan 23, 2023

same issue here

1 similar comment
@AlexDochioiu
Copy link

same issue here

@ch-muhammad-adil
Copy link

Happening with me too when application goes in background and brought back in foreground after sometime, not sure if this is http or network?

@dr0-dev
Copy link

dr0-dev commented Feb 1, 2023

why is this ticket still open and not assigned to anyone?

@willbryant
Copy link

@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?

@Frankdroid7
Copy link

Is there any update on this open issue please?

@brianquinlan
Copy link
Collaborator

brianquinlan commented Feb 13, 2023

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.

  1. improve the documentation on this
  2. automatically retry (that's a bit dangerous - it might mask real errors because EBADF isn't just for reclaimed sockets
  3. invalidate the HttpClient connection pool when the application is suspected - will reduce performance in some cases, would require coordination with flutter

Something else?

@natebosch Any ideas?

@brianquinlan
Copy link
Collaborator

brianquinlan commented Feb 13, 2023

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
Copy link
Collaborator

brianquinlan commented May 9, 2024

@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

@hls-app
Copy link

hls-app commented May 12, 2024

@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.

@brianquinlan
Copy link
Collaborator

@michael-joseph-payne

I agree that this is an issue but it isn't one that can be easily fixed with IOClient. In #197 (comment), I suggested using package:cupertino_http to resolve this issue.

In the documentation for package:http we do list the native implementations, i.e. package:cupertino_http in this case.

Could you be more explicit on what actions I should take to make this better?

Thanks!

P.S. I spend a lot of time trying to make the Dart HTTP ecosystem better and I find your comment unkind. I also don't know what it means to "acknowledge" an issue but this is my 13th comment on this issue so I'm definitely aware that it exists ;-)

@michael-joseph-payne
Copy link

@michael-joseph-payne

I agree that this is an issue but it isn't one that can be easily fixed with IOClient. In #197 (comment), I suggested using package:cupertino_http to resolve this issue.

In the documentation for package:http we do list the native implementations, i.e. package:cupertino_http in this case.

Could you be more explicit on what actions I should take to make this better?

Thanks!

P.S. I spend a lot of time trying to make the Dart HTTP ecosystem better and I find your comment unkind. I also don't know what it means to "acknowledge" an issue but this is my 13th comment on this issue so I'm definitely aware that it exists ;-)

Do you work on the Flutter team? I don't think it is unkind at all to express disappointment. Additionally, this issue has never been triaged, assigned, or otherwise updated past your comment to try another library. As much as you may not like the wording in my post, those are facts - there is no way for a person coming to this thread to resolve this issue.

@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.

Do you have a reply to this? Because this is where it ended for me too. It also reinforces what I'm saying. One can post all they want - will one get a follow up? This is why I made the comments I did in the second paragraph.

There's still no resolution for me. No leads. And the one follow up I got said they didn't like what I said. I don't know what I could do better either.

Either way, thank you for your work on the platform.

@brianquinlan
Copy link
Collaborator

@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.

I don't think that it is possible for you to be using package:cupertino_http and get that exception. ClientSocketException is defined as part of IOClient and is only used there

@brianquinlan
Copy link
Collaborator

Do you work on the Flutter team? I don't think it is unkind at all to express disappointment. Additionally, this issue has never been triaged, assigned, or otherwise updated past your comment to try another library. As much as you may not like the wording in my post, those are facts - there is no way for a person coming to this thread to resolve this issue.

I work on the Dart team and I think that using package:cupertino_http will resolve the issue.

[snipped]

Do you have a reply to this? Because this is where it ended for me too. It also reinforces what I'm saying. One can post all they want - will one get a follow up? This is why I made the comments I did in the second paragraph.

I just replied: #197 (comment)

There's still no resolution for me. No leads. And the one follow up I got said they didn't like what I said.

Did you try using package:cupertino_http? Because that seems like a clear lead to me and seems to have resolved the issue for several people.

I don't know what I could do better either.

Were you able to reproduce the issue using package:cupertino_http? If so, could you attach a link to a complete but minimal reproduction? That would be very helpful!

@michael-joseph-payne
Copy link

michael-joseph-payne commented Aug 7, 2024

Do you work on the Flutter team? I don't think it is unkind at all to express disappointment. Additionally, this issue has never been triaged, assigned, or otherwise updated past your comment to try another library. As much as you may not like the wording in my post, those are facts - there is no way for a person coming to this thread to resolve this issue.

I work on the Dart team and I think that using package:cupertino_http will resolve the issue.

[snipped]

Do you have a reply to this? Because this is where it ended for me too. It also reinforces what I'm saying. One can post all they want - will one get a follow up? This is why I made the comments I did in the second paragraph.

I just replied: #197 (comment)

There's still no resolution for me. No leads. And the one follow up I got said they didn't like what I said.

Did you try using package:cupertino_http? Because that seems like a clear lead to me and seems to have resolved the issue for several people.

I don't know what I could do better either.

Were you able to reproduce the issue using package:cupertino_http? If so, could you attach a link to a complete but minimal reproduction? That would be very helpful!

I have cases where I'm using http only, and yes there does seem to be some relief if that is one's only dependency.

My other case is slightly more complicated - but I get tens of thousands of these errors when using it - I am using Websocket, similar to these posts:

#1282
#916

I agree with them that this is useful, but only partly implemented. Is there something we can doing to improve the situation?

The easiest way I've found to cause these errors is by connecting like below, with the IP address unavailable (causes either WebsocketException or SocketException, depending on the connection type):

Future<void> connect() async {
  final url ='wss://url.com';
  final headers = <String, dynamic>{
    'Content-Type': 'application/json',
    'Authorization': 'Bearer $jwt',
  };

  _webSocket = await WebSocket.connect(url, headers: headers);
  _webSocket!.pingInterval = pingInterval;
  _webSocketChannel = IOWebSocketChannel(_webSocket!);
}

@brianquinlan
Copy link
Collaborator

brianquinlan commented Aug 7, 2024

I agree with them that this is useful, but only partly implemented. Is there something we can doing to improve the situation?

I think that the problem is that you are still using socket-based transport. Have you tried using CupertinoWebSocket with AdapterWebSocketChannel?

@michael-joseph-payne
Copy link

I agree with them that this is useful, but only partly implemented. Is there something we can doing to improve the situation?

I think that the problem is that you are still using socket-based transport. Have you tried using CupertinoWebSocket with AdapterWebSocketChannel?

I believe you're right. I'm going to need to take some time to evaluate, as this is a very burly part of our codebase (it is an IoT based product).

I didn't think we had this option when I looked around a few months ago. Thank you for adding it.

@corepuncher
Copy link

corepuncher commented Sep 15, 2024

UPDATE: native_dio_adapter fixed it! Not only do I no longer have strange iOS behavior after restarting app, but I refactored the dio instance to be a singleton (without closing it so often) and boom, everything running very fast.

@brianquinlan
Copy link
Collaborator

UPDATE: native_dio_adapter fixed it! Not only do I no longer have strange iOS behavior after restarting app, but I refactored the dio instance to be a singleton (without closing it so often) and boom, everything running very fast.

FWIW, (package:native_dio_adapter uses package:cronet_http and package:cupertino_http as implementation details](https://github.com/cfug/dio/blob/main/plugins/native_dio_adapter/pubspec.yaml)

@thorito
Copy link

thorito commented Oct 16, 2024

Hi, I have read the previous messages.

I am using mqtt5client: ^4.5.3 and I am facing the same problem on iOS.

SocketException: Bad file descriptor (OS Error: Bad file descriptor, errno = 9), address = <redacted domain>, port = 52852.

Do you know if there is a solution?

Any information is appreciated.

@sahinemin
Copy link

sahinemin commented Oct 18, 2024

I am using web_socket_channel 3.0.1 and got WebSocketChannelException: SocketException: Bad file descriptor (OS Error: Bad file descriptor, errno = 9), address =, port = 61293 on ios

flutter version 3.24.3

@MichalDanielDobrzanski
Copy link

6 years and still time is ticking...

@brianquinlan
Copy link
Collaborator

6 years and still time is ticking...

Hi @MichalDanielDobrzanski

Did you try using package:cupertino_http?

@michael-joseph-payne
Copy link

michael-joseph-payne commented Oct 22, 2024

There is definitely a significant reduction in exceptions when using native_dio_adapter, but it does not resolve all occurrences of HttpException / SocketException (bad file descriptor).

As an example:

DioException [unknown]: null Error: HttpException: Bad file descriptor uri = <redacted>

happens when there was a previous exception:

DioException [bad response]: This exception was thrown because the response has a status code of 401 and RequestOptions.validateStatus was configured to throw for this status code.
The status code of 401 has the following meaning: "Client error - the request contains bad syntax or cannot be fulfilled"
Read more about status codes at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
In order to resolve this exception you typically have either to verify and fix your request code or you have to fix the server code.

We did however, see a reduction of these type of events from thousands a month to just a handful. Definitely an improvement.

@mx1up
Copy link

mx1up commented Oct 22, 2024

we can confirm using package:cupertino_http fixes the problem.

Maybe a note could be added to the documentation that using this package on ios is highly recommended, otherwise problems like the one described in this issue can occur. (also explicitly link to this issue)

@jamesncl
Copy link

I'm also getting Bad file descriptor errors from some users, but for me it is a ClientException:

ClientException: Bad file descriptor

With the following stacktrace (I'm using version http: ^1.2.2):

io_client.dart in IOClient.send at line 156 within http
base_request.dart in BaseRequest.send at line 133 within http

The majority are from iPhone users but I also get this from a smaller proportion of Android users. This may technically be a slightly different issue because it's a ClientException not OS Error as raised by the OP, but I thought it was relevant to post here because the problem I had was the vague / misleading error message which is one of the points raised in the original issue. I received this error in a part of my code which calls Google Drive APIs to upload files, and I spent several days going on a wild goose chase because I assumed Bad file descriptor meant there was some kind of corruption in the files being uploaded resulting in them not matching their mime type. So, a more accurate error message would be very helpful!

@aipok
Copy link

aipok commented Mar 15, 2025

In my projects, I'm seeing 90/10%, where 90% of the issues are happening on the iOS devices and 10% on Samsung devices. The issue is most of the time occurred while user holds app in the background for a long period of time and wakes it up while receiving the push or just start the app again, this error occurs or the one that indicates the domain found issue.
ClientException with SocketException: Failed host lookup: <domain> (OS Error: No address associated with hostname, errno = 7)
or
ClientException with SocketException: Bad file descriptor (OS Error: Bad file descriptor, errno = 9), address = <domain>

After this issue occurred the user getting the same error until he restart the app or waits for some time like 5 minutes or so. At least this is what I was seeing, while catch the same error couple of times on my devices. Worth to note as well, that all the network checks are saying at the same time that user has network connection wifi or LTE doesn't matter.

Devices breakdown looks like this...
Image

The issue exists for a few years at least (while I joined the Flutter devs community) and I still didn't figure out how to solve it. The problem with usage of the cupertino_http package is also not always possible, because often the error occurred during the Firebase library data sync and fetching. And Google doesn't provide a way to force specific http library to use. It would be nice if the cupertino_http could be automatically used by the http client for iOS devices out of the box. At least it could solve a lot of problems on iOS devices.

@brianquinlan
Copy link
Collaborator

Hey @aipok

Thanks for the data! What firebase package are you using? The packages that I found use package:http and allow you to provide your own Client.

@aipok
Copy link

aipok commented Mar 18, 2025

@brianquinlan another stopper in this case, which I didn't found how to solve, other as to rewrite everything, is the retrofit and dio packages, which are used for API integration in my projects. Retrofit is working with Dio objects directly and Dio on the other side is using the HttpClient instead of just Client and I cannot give it the cupertino_http client instance because of the wrong type.
Like following code

(httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
      final HttpClient httpClient;
      if (Platform.isIOS || Platform.isMacOS) {
        final config = URLSessionConfiguration.ephemeralSessionConfiguration()
          ..cache = URLCache.withCapacity(memoryCapacity: 2 * 1024 * 1024);
        httpClient = CupertinoClient.fromSessionConfiguration(config);  <=== this part is mapped to the Client instance, while expected is HttpClient instance
      } else {
        httpClient = HttpClient();
      }
      return httpClient;
    };

The IOHttpClientAdapter is expecting HttpClient and not the Client, which makes it impossible to add cupertino package :(
I did looked over the Dio implementation, but didn't found an implementation which is using Client. I understand, that it is more like a question to the Dio authors, but overall this is a problem for everyone who is using dart http on Flutter and facing similar issues without even knowing they exists in the first place.
To rewrite it fully on the new way, takes a lot of time, which is hard to get in terms of size of the project. Also removing retrofit from the project eventually means to get back a lot of boilerplate code it removes.

@aipok
Copy link

aipok commented Mar 18, 2025

@brianquinlan as for Firebase packages. The most used and where we got a lot of errors are the firebase_remote_config and firebase_messaging.

@aipok
Copy link

aipok commented Mar 18, 2025

@brianquinlan My personal opinion about the issue or at least it feels like this, that the real problem is hidden somewhere in between Flutter and Native layers. It really feels that my app process was blocked from the network access completely, while I got this error on my device. Because I was able to use any other apps at the same time and they had no problem with the connection, but my app did have the issue and changing the network during the error also have no effect. App process just have no access to the system network. While the error exists nothing able to get to the network, not the http calls, nor the websockets etc.
Hope the information I provide will move it somewhere 🤞

@kuhnroyal
Copy link

@brianquinlan
Copy link
Collaborator

@aipok

From looking at the code, it seems like the Firebase products are using native plugins for transport so this issue is not related to dart.

As @kuhnroyal pointed out, package:native_dio_adapter provides the package:dio interface using package:cupertino_http and package:cronet_http as transport.

@aipok
Copy link

aipok commented Mar 19, 2025

@kuhnroyal @brianquinlan
thank you, will give it a try and let you know.

@AlexV525
Copy link
Contributor

AlexV525 commented Apr 1, 2025

This is also happening with package:web_socket.

@brianquinlan Do you have any idea how to handle this issue in a more general way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests