Skip to content

Fix source field typespecs in HTTPError and TransportError#373

Open
cgarvis wants to merge 1 commit into
sneako:mainfrom
cgarvis:fix/typespec-source-fields
Open

Fix source field typespecs in HTTPError and TransportError#373
cgarvis wants to merge 1 commit into
sneako:mainfrom
cgarvis:fix/typespec-source-fields

Conversation

@cgarvis
Copy link
Copy Markdown

@cgarvis cgarvis commented May 13, 2026

The source field in Finch.HTTPError and Finch.TransportError was typed as Mint.HTTPError.t() | nil and Mint.TransportError.t() | nil respectively. The set-theoretic type checker surfaced by elixir-lang/elixir#15366 treats @type t() definitions as closed-map types. Because Mint.HTTPError.t() only declares {reason: reason()} and omits the :module field present in the actual struct, a value matched with %Mint.HTTPError{reason: reason, module: module} = error carries a :module field that falls outside the declared type. This produces a :badstructfield diagnostic for the :source field at the construction site.

The :source field holds the original Mint exception so that Exception.message/1 can delegate to it. Exception.t() is the accurate type for that usage: it is an open-map type that admits any exception struct, including both Mint.HTTPError and Mint.TransportError.

Before:

# http_error.ex
@type t() :: %__MODULE__{
        reason: reason(),
        module: module() | nil,
        source: Mint.HTTPError.t() | nil
      }

# transport_error.ex
@type t() :: %__MODULE__{reason: reason(), source: Mint.TransportError.t() | nil}

After:

# http_error.ex
@type t() :: %__MODULE__{
        reason: reason(),
        module: module() | nil,
        source: Exception.t() | nil
      }

# transport_error.ex
@type t() :: %__MODULE__{reason: reason(), source: Exception.t() | nil}

mix test: 206 tests, 0 failures, 1 skipped.

Mint.HTTPError.t() and Mint.TransportError.t() only declare the :reason
field in their @type t() definitions, omitting other struct fields (e.g.
:module in Mint.HTTPError). When the set-theoretic type checker resolves
these as closed-map types, a value matched via a struct pattern carries
extra fields that fall outside the declared type, producing a
:badstructfield diagnostic.

The :source field stores the original Mint exception so that
Exception.message/1 can delegate to it. Exception.t() is the accurate
type for that usage and resolves the incompatibility.
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

Successfully merging this pull request may close these issues.

1 participant