Skip to content

[http] Fix incorrect IOClient documentation for non-2xx HTTP responses - #1948

Open
EchoEllet wants to merge 2 commits into
dart-lang:masterfrom
EchoEllet:patch-1
Open

[http] Fix incorrect IOClient documentation for non-2xx HTTP responses#1948
EchoEllet wants to merge 2 commits into
dart-lang:masterfrom
EchoEllet:patch-1

Conversation

@EchoEllet

Copy link
Copy Markdown
Contributor

Fixes #1947


  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:

Many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.

Note: The Dart team is trialing Gemini Code Assist. Don't take its comments as final Dart team feedback. Use the suggestions if they're helpful; otherwise, wait for a human reviewer.

@EchoEllet EchoEllet changed the title docs(http): fixes invalid doc comment of IOClient [http] Fix incorrect IOClient documentation for non-2xx HTTP responses Jul 4, 2026

@brianquinlan brianquinlan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this!

Comment thread pkgs/http/lib/src/io_client.dart Outdated
Comment thread pkgs/http/lib/src/io_client.dart Outdated
/// // Exception is transport-related, check `e.osError` for more details.
/// } on http.ClientException catch (e) {
/// // Exception is HTTP-related (e.g. the server returned a 404 status code).
/// // Exception is transport-related.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous comment was false but I don't think that the new comment is correct either. I think that the exception could be transport-related but it could also be HTTP-related, for example a failure to parse the response message. Maybe restore the previous SocketException on-clause and change this to.

// Exception is HTTP-related (e.g. the client could not parse the server's response).

Maybe there is a better (or other) example.

@EchoEllet EchoEllet Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that the new comment is correct either
I think that the exception could be transport-related but it could also be HTTP-related

Indeed. Some BaseClient implementations may throw ClientException in the event of a TLS handshake error, whereas IOClient (or HttpClient from dart:io) throws TlsException.

// Exception is HTTP-related (e.g. the client could not parse the server's response).

I propose:

// Exception is transport-related (e.g., no internet connection or server is unreachable)
// or HTTP protocol-related (e.g., redirect processing failure, such as a missing
// Location header).

Since both io.SocketException and io.HttpException (and by extension io.RedirectException) are mapped to ClientException from the http package.

Maybe restore the previous SocketException on-clause and change this to.

Makes sense. However, I suggest adding a note indicating remove if not needed, since this is already covered by http.ClientException. Many developers assume they need to handle both when seeing the code.

Places that may throw ClientException in IOClient:

  1. When sending a request after closing the client
/// Sends an HTTP request and asynchronously returns the response.
  @override
  Future<IOStreamedResponse> send(BaseRequest request) async {
    if (_inner == null) {
      throw ClientException(
          'HTTP request failed. Client is already closed.', request.url);
    }
}

Maybe it should throw a Dart error (e.g., StateError) instead of ClientException, since this is typically considered a programming bug?

When I sent a PR to flutter/packages, the preferred approach was to use Errors for programming bugs (e.g., flutter/packages#8079)

  1. When HttpClient from dart:io throws
} on SocketException catch (error) {
      throw _ClientSocketException(error, request.url);
    } on HttpException catch (error) {
      throw ClientException(error.message, error.uri);
    }

I assume HttpException means something went wrong at the HTTP protocol level.

There is also RedirectException from dart:io:

class RedirectException implements HttpException {
  final String message;
  final List<RedirectInfo> redirects;

  const RedirectException(this.message, this.redirects);
  // ...
}

Which suggests that an HTTP response was at least received?
I'm not sure if HttpException indicates that a response was received.

In either case, this does not suggest statusCode >= 300 (not a successful response or non-2xx).

EchoEllet and others added 2 commits July 7, 2026 03:19
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

PR Health

Unused Dependencies ⚠️
Package Status
http
❗ Show Issues
These packages may be unused, or you may be using assets from these packages:
* dart_flutter_team_lints
* shelf

For details on how to fix these, see dependency_validator.

This check can be disabled by tagging the PR with skip-unused-dependencies-check.

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?
http None 1.6.0 1.6.1-wip 1.6.1-wip ✔️

This check can be disabled by tagging the PR with skip-breaking-check.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbol Leaking sources

This check can be disabled by tagging the PR with skip-leaking-check.

Changelog Entry
Package Changed Files
package:http pkgs/http/lib/src/io_client.dart

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

Coverage ✔️
File Coverage
pkgs/http/lib/src/io_client.dart 💚 89 %

This check for test coverage is informational (issues shown here will not fail the PR).

This check can be disabled by tagging the PR with skip-coverage-check.

License Headers ✔️
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
Files
no missing headers

All source files should start with a license header.

Unrelated files missing license headers
Files
pkgs/cupertino_http/example/example.dart
pkgs/http/example/main.dart
pkgs/http_multi_server/test/cert.dart

This check can be disabled by tagging the PR with skip-license-check.

@brianquinlan

Copy link
Copy Markdown
Collaborator

A bit of context...

I introduced _ClientSocketException because IOClient.send was not catching SocketException from dart:io so people where writing code like in the example:

 try {
   data = await client.read(Uri.https('example.com', ''));
 } on SocketException catch (e) {
 } on http.ClientException catch (e) {
 }

But, that is not correct for any client other than IOClient. So _ClientSocketException was designed as a way to be backwards-compatible with existing code (since it implements SocketException) while allowing people to only catch ClientException (since it extends ClientException) if the wanted to support Client's other than IOClient.

My expectation is that most applications should just catch ClientException and you should only catch SocketException if:

  1. you know that you are only working with IOClient
  2. you need to extract osError

Throwing ClientException if the client is already closed is probably the wrong thing (that change predates me working on package:http) but fixing it would be breaking and it probably isn't worth it.

For

// Exception is transport-related (e.g., no internet connection or server is unreachable)
// or HTTP protocol-related (e.g., redirect processing failure, such as a missing
// Location header).

Which exception does that apply to? Internet connection issues should be SocketException while HTTP protocol-related issues should be ClientException, right?

@EchoEllet

EchoEllet commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the context.

just catch ClientException and you should only catch SocketException if:

you know that you are only working with IOClient
you need to extract osError

I agree.

My expectation is that most applications should just catch

There is an exception

As far as I have checked, catching ClientException is not sufficient to cover TlsException.

At least in my case, I workarouned this with a conditional import (see also this) to cover TLS failures on IO implementation. Does not appear to be an issue with BrowserClient or OkHttpClient since it always throws a ClientException in case of a TLS failure.

Throwing ClientException if the client is already closed is probably the wrong thing (that change predates me working on package:http) but fixing it would be breaking, and it probably isn't worth it.

I would still consider it or add a comment indicating the reason why this is not a Dart error (e.g., StateError). Application developers should probably attempt to fix this programming bug rather than causing it to display an error message to the end user as if it were an HTTP protocol-related or transport error.

Many applications keep the client open for the lifetime of their application, so it may not even close.

Which exception does that apply to? Internet connection issues should be SocketException while HTTP protocol-related issues should be ClientException, right?

Yes, but ClientException should cover both cases unless I'm missing anything.

@brianquinlan

Copy link
Copy Markdown
Collaborator

We should probably catch TlsException and rethrow it (possible as a new exception type, in the same pattern as _ _ClientSocketException. Why don't you make the changes as you want and I'll review?

@EchoEllet

Copy link
Copy Markdown
Contributor Author

Sure. Maybe in a separate PR since this is only focused on clarifying the comment.

/// // Exception is transport-related, check `e.osError` for more details.
/// } on http.ClientException catch (e) {
/// // Exception is HTTP-related (e.g. the server returned a 404 status code).
/// // Exception is transport-related.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always transport-related? How about just remove this line.

And then wrap the next two lines to 80 columns and I'll merge!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[http] IOClient doc comment incorrectly states that non-2xx responses throw ClientException

2 participants