Skip to content

Flutter networking errorFlutter: “HttpException: Connection closed before full header was received”

An HTTP request from a Flutter or Dart app fails with HttpException: Connection closed before full header was received (with package:http it arrives as a ClientException). The connection opened, then closed before the server sent a response. Either the server or a proxy dropped it, or the client reused a keep-alive connection the server had already closed.

Short answer

Check the server log for the request first: a crash, a timeout or a request size limit closes the connection without a response. If it fails only now and then, the server is closing idle keep-alive connections: retry the request once with RetryClient, and make sure the URL scheme matches the port (http vs https). For local servers on the emulator, use 10.0.2.2.

Why the connection closes early, and the fix

  1. 01

    The server crashed or timed out on this request

    A server that throws, runs out of memory, or hits a proxy or load balancer timeout can close the socket without answering. The server or proxy log for that time shows the real error; fix it there. Large uploads can also hit a body size limit (for example nginx client_max_body_size).

  2. 02

    A reused keep-alive connection the server already closed

    “Gets and sets the idle timeout of non-active persistent (keep-alive) connections. The default value is 15 seconds.” Dart, HttpClient.idleTimeout

    Dart keeps connections open for reuse. If the server closes idle connections sooner than the client stops reusing them, the next request on that connection fails at random. Retry safe requests once, and lower the client idle timeout below the server’s by passing your own HttpClient through IOClient (mobile and desktop; dart:io isn’t available on the web).

    import 'dart:io';
    import 'package:http/http.dart' as http;
    import 'package:http/io_client.dart';
    import 'package:http/retry.dart';
    
    final inner = HttpClient()..idleTimeout = const Duration(seconds: 5);
    final client = RetryClient(
      IOClient(inner),
      retries: 2,
      whenError: (error, _) => error is http.ClientException,
    );
  3. 03

    Retrying without a plan

    “package:http/retry.dart provides a class RetryClient to wrap an underlying http.Client which transparently retries failing requests.” pub.dev, package:http

    RetryClient only retries on a 503 response by default, so connection errors need whenError. Retry only requests that are safe to repeat, such as GET, or POSTs your server deduplicates.

  4. 04

    http sent to an https port, or the wrong host

    Plain http to a port that expects TLS (or the reverse) makes the server drop the connection. Check the scheme and port, and on the Android emulator call your computer at 10.0.2.2, not localhost.

Checked against the vendor’s docs on : Dart API: HttpClient.idleTimeout; pub.dev: package:http (Client, RetryClient).

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Flutter “SocketException: Connection refused” · Flutter “SocketException: Failed host lookup” · Flutter “HandshakeException: CERTIFICATE_VERIFY_FAILED” · Flutter “type 'Null' is not a subtype of type 'String'”

Questions

More on this error

Should I create a new http.Client for every request?

No. package:http recommends one Client for many requests to the same server, closed when you are done. A new client per request hides the keep-alive issue but costs a fresh connection each time.

It only happens on the emulator with my local server. Why?

Local dev servers often close connections fast or restart on file changes. Check the server console when it fails, and retry once in development.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.