Skip to content

HTTP.Exceptions submodule removed in 2.x — restore as a (deprecating) compat shim, or document it? #1314

@mathieu17g

Description

@mathieu17g

In 1.x, HTTP.Exceptions was an internal organizational submodule (include("Exceptions.jl"); using .Exceptions, like Messages/Connections), and the using re-exported the types to the top level. The documented names were the top-level ones — HTTP.StatusError, HTTP.ConnectError, HTTP.TimeoutError — and those still resolve in 2.x (v2.3.0). What's gone is the HTTP.Exceptions submodule itself (isdefined(HTTP, :Exceptions) == false), so code reaching in via HTTP.Exceptions.StatusError breaks.

It's easy to miss because the reference sits in a catch block:

try
    HTTP.get(url)
catch e
    if e isa HTTP.Exceptions.StatusError && e.status == 429
        ...
    end
end

That compiles, precompiles, and passes its happy-path tests, then throws UndefVarError: Exceptions not defined in HTTP the first time a real error reaches the catch — silent until production, unlike the readtimeout deprecation which warns. The migration guide covers readtimeoutread_idle_timeout but not this — and it's not rare: a GitHub code search returns 40 repos using HTTP.Exceptions.StatusError and 13 using HTTP.Exceptions.RequestError, including AWS.jl, PromptingTools.jl, and WebSockets.jl (this org).

The 7 names 1.x's Exceptions exported split cleanly:

name(s) in 2.x suggested
HTTPError · StatusError · ConnectError · TimeoutError top-level in HTTP deprecating re-export (below)
RequestError removed, no equivalent ¹ migration-guide note
@try · current_exceptions_to_string removed copy back if wanted (trivial)

¹ RequestError (thrown, fields .request/.error) has no drop-in: 2.x lets the underlying exception propagate, with isrecoverable(err) as the classifier (made public in #1310). RequestRetryError is the retry_if wrapper (field .err), not what's thrown — so restoring it means reintroducing the type and its throw sites, a behavior change, not a shim.

For the four that just moved, a small deprecating submodule keeps old code working and nudges migration (slots into src/HTTP.jl before end # module):

module Exceptions
    import ..HTTP
    Base.@deprecate_binding StatusError  HTTP.StatusError  false
    Base.@deprecate_binding ConnectError HTTP.ConnectError false
    Base.@deprecate_binding TimeoutError HTTP.TimeoutError false
    Base.@deprecate_binding HTTPError    HTTP.HTTPError    false
end

HTTP.Exceptions.StatusError resolves again, forwards to HTTP.StatusError, and depwarns under --depwarn=yes / in tests — silent in production.

This mirrors the manual-Cookie gap (#1283), where the docs-only note (#1284) gave way to restoring the behavior (#1287); and #1267, where you were "open to … easy/simple things we can add back to 2.X for compat." So: the deprecating shim for the four moved types plus a migration-1x.md note for RequestError, or a note only? Happy to PR either.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions