Skip to content

fix(error): replace the opaque I/O error with actionable diagnostics #284

Closed

Description

@inureyes

Part of the OpenSSH drop-in compatibility epic.

Problem

20 of the 48 genuine failures in the 2026-08-26 measurement surface as nothing more than Error: I/O error. The same string covers connection refusal, host key rejection, authentication failure and mid-session channel errors, so it distinguishes none of them.

Reproduced with no listener on the port, where OpenSSH says Connection refused:

X Error: I/O error

This cost real time during the measurement itself. Establishing that a cluster of failures was host key file selection rather than transport required attaching a live sshd and reading the server's log, because the client would not say which layer had failed. That is the cost every user of bssh pays on every failure.

Scope

  • Replace the blanket I/O error with errors that name the layer and the cause: DNS resolution, TCP connect (carrying the OS error), protocol or version negotiation, host key verification, authentication (naming the methods tried and refused), channel open, and command execution.
  • Carry the underlying std::io::Error kind and the OS errno through instead of collapsing them at the boundary. The refused-connection case must read as refused.
  • Match OpenSSH's wording where a script might reasonably match on it, and keep bssh-specific detail on additional lines rather than replacing the recognizable first line.
  • Report authentication failures with the method list, the way ssh does, since "no supported methods remain" and "the key was rejected" are different problems.
  • Preserve the exit status contract while doing this: connection and authentication failures exit 255, and a remote command's own status passes through unchanged.

Acceptance criteria

  • Connection refused, host unreachable, DNS failure, host key mismatch, and authentication exhaustion each produce a distinct message naming the cause.
  • The OS error is included where one exists.
  • No code path reports a bare I/O error, enforced by a test over the error enum.
  • Exit status 255 for client-side failures, and the remote status otherwise, covered by tests.

Part of #275

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions