Skip to content

Surface MSSQL token detail when the connection closes#185

Open
kevinkoltz wants to merge 2 commits into
elixir-ecto:masterfrom
kevinkoltz:tds-unmask-disconnect-errors
Open

Surface MSSQL token detail when the connection closes#185
kevinkoltz wants to merge 2 commits into
elixir-ecto:masterfrom
kevinkoltz:tds-unmask-disconnect-errors

Conversation

@kevinkoltz
Copy link
Copy Markdown
Contributor

SQL Server frequently sends an error token immediately before closing the connection (login failure, fatal severity, kill by DBA). The token was parsed and surfaced to the caller of the failing statement, then dropped when the socket closed — the disconnect error reported only "Connection closed." / "tcp closed" / "Connection failed to receive packet due :closed", forcing operators to dig through server-side logs to learn the actual reason.

This change carries the most recent token on the protocol state (last_mssql_error, captured in the existing msg_error handler and cleared on checkout/1) and uses it when building the disconnect error at the five masking sites in protocol.ex (ping/1, the two handle_info clauses for :tcp_closed / :ssl_closed / :tcp_error / :ssl_error, the flush/1 receive, and the msg_recv/1 socket read-error path). The disconnect-vs-stop return semantics are unchanged, so DBConnection's pool-managed reconnect continues to work exactly as before.

Tds.Error.message/1 gains a head that combines :message and :mssql when both are populated, so existing callers that pattern-match on either field individually keep working while connection-close errors now surface the underlying server reason. The error_details typespec is broadened to reflect the full set of fields the token stream decodes in Tokens.decode_error/2.

kevinkoltz and others added 2 commits May 29, 2026 09:51
SQL Server frequently sends an error token immediately before closing
the connection (login failure, fatal severity, kill by DBA). The token
was parsed and surfaced to the caller of the failing statement, then
dropped when the socket closed — the disconnect error reported only
"Connection closed." / "tcp closed" / "Connection failed to receive
packet due :closed", forcing operators to dig through server-side logs
to learn the actual reason.

This change carries the most recent token on the protocol state
(`last_mssql_error`, captured in the existing `msg_error` handler and
cleared on `checkout/1`) and uses it when building the disconnect error
at the five masking sites in `protocol.ex` (`ping/1`, the two
`handle_info` clauses for `:tcp_closed` / `:ssl_closed` / `:tcp_error` /
`:ssl_error`, the `flush/1` receive, and the `msg_recv/1` socket
read-error path). The disconnect-vs-stop return semantics are unchanged,
so DBConnection's pool-managed reconnect continues to work exactly as
before.

`Tds.Error.message/1` gains a head that combines `:message` and
`:mssql` when both are populated, so existing callers that pattern-match
on either field individually keep working while connection-close errors
now surface the underlying server reason. The `error_details` typespec
is broadened to reflect the full set of fields the token stream decodes
in `Tokens.decode_error/2`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub Actions runners now ship with a pre-existing
/etc/apt/sources.list.d/microsoft-prod.list that references
signed-by=/usr/share/keyrings/microsoft-prod.gpg. The workflow added a
second source for the same repository signed by a key in
/etc/apt/trusted.gpg.d, which apt rejected as a conflicting signed-by
configuration (exit code 100 from apt-get update).

Remove the runner-provided list and install the key into the expected
keyrings location with an explicit signed-by entry to match. Same fix
adopted by elixir-ecto#184 to unblock CI on PRs against master.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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