Skip to content

fix: surface connection failures and clean failed mounts - #21

Merged
uhs-robert merged 10 commits into
mainfrom
fix/10-connection-failure-cleanup
Sep 11, 2026
Merged

uhs-robert merged 10 commits into
mainfrom
fix/10-connection-failure-cleanup

Conversation

@uhs-robert

@uhs-robert uhs-robert commented Aug 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #10 by surfacing native SSH/SSHFS failures and cleaning up only mount directories owned by the failed connection attempt.

Changes

  • preserve the first non-empty SSH process output when stderr is empty
  • surface SSHFS exit codes while preserving stdout and stderr separately
  • fail fast only for unresolved-host errors instead of opening interactive authentication
  • preserve the existing interactive-auth fallback for all other batch failures
  • return mount-directory ownership from MountPoint.get_or_create()
  • make mount-directory ownership race-safe across concurrent Neovim processes
  • clear PRE_MOUNT_DIRS state after failed setup
  • remove only empty, inactive mount directories created by the failed attempt
  • make failed-directory cleanup best-effort so cleanup errors do not mask the connection error
  • report a connections.socket_dir that exceeds the Unix socket path limit from :checkhealth

Socket path length

ControlMaster sockets are Unix domain sockets, so the kernel caps their path at sun_path: 108 bytes on Linux, 104 on macOS and the BSDs. SSH appends /%C (a 40 character hash) plus a temporary suffix while creating one, which leaves 49 characters for connections.socket_dir on Linux and 45 on macOS.

Past that, every connection fails with:

unix_listener: path "/…/sockets/280c4fee….yp8WhV7cdz7iQXdQ" too long for Unix domain socket

That message names neither the setting at fault nor the limit involved, and it surfaces as an authentication failure rather than a configuration error. :checkhealth sshfs now reports it directly: an error past the budget, a warning within ten characters of it, and the measured budget either way.

Testing

Regression coverage is included, on top of the shared harness from #25. Run it with make test.

  • unresolved-host diagnostics in both the Linux and macOS resolver wordings, skipping interactive authentication while other batch failures still reach it
  • authentication failures reporting both the batch and interactive exit codes
  • SSHFS failures preserving the exit code, preferring stderr over stdout, ignoring whitespace-only output, and still reporting a code when the process says nothing
  • mount-directory ownership from get_or_create, including the concurrent-creation race and the non-raising failure path
  • cleanup removing only a directory this attempt created and left empty, never one that pre-existed, holds contents, or turns out to be an active mount
  • a cleanup error not masking the connection error, and a failed attempt never reaching the lockfile
  • the successful connection path
  • the socket path length check: short paths, the exact limit, the warning band, the smaller macOS budget, and that a missing ~/.ssh does not hide a broken socket path

Closes #10

@uhs-robert uhs-robert closed this Aug 30, 2026
@uhs-robert
uhs-robert force-pushed the fix/10-connection-failure-cleanup branch from 8c5248b to 57f5862 Compare August 30, 2026 12:09
@uhs-robert uhs-robert reopened this Aug 30, 2026
@uhs-robert
uhs-robert force-pushed the fix/10-connection-failure-cleanup branch from 2b3addc to fd7a38c Compare August 30, 2026 12:16
@uhs-robert
uhs-robert force-pushed the fix/10-connection-failure-cleanup branch from 47136db to 3f60a25 Compare August 30, 2026 12:36
@uhs-robert
uhs-robert requested a lite review from Copilot August 30, 2026 12:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The PR description states it must have regression coverage delivered via #24 before it is considered ready to merge, and those tests are not included here.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves sshfs.nvim’s connection failure diagnostics (surfacing native SSH/SSHFS exit codes and output) and tightens failure cleanup to only remove mount directories created by the failed connection attempt.

Changes:

  • Preserve the first non-empty SSH process output (stderr preferred, otherwise stdout) to avoid losing diagnostics when stderr is an empty string.
  • Surface SSHFS non-zero exit codes and preserve stdout/stderr separately in failure results.
  • Track whether a mount directory was created by the current attempt and, on failure, best-effort remove only empty/inactive directories created by that attempt while clearing PRE_MOUNT_DIRS state.
File summaries
File Description
lua/sshfs/session.lua Clears pre-mount bookkeeping on failures and removes only empty/inactive mount dirs created by the failed attempt.
lua/sshfs/lib/sshfs.lua Adds unresolved-host fast-fail and enriches mount/auth failure results with exit codes and separated output.
lua/sshfs/lib/ssh.lua Introduces a helper to select the first non-empty trimmed process output (fixing empty-stderr masking).
lua/sshfs/lib/mount_point.lua Extends get_or_create() to return both readiness and whether the directory was created by this call.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lua/sshfs/lib/ssh.lua
@uhs-robert
uhs-robert force-pushed the fix/10-connection-failure-cleanup branch from e756885 to 9c0fa10 Compare August 30, 2026 12:51
`vim.fn.mkdir` raises E739 rather than returning 0 on failure, so the
non-"p" leaf creation threw out of Session.connect instead of reporting
"Failed to create mount directory", and the creation-race fallback was
unreachable. Wrap both mkdir calls in pcall so the function returns
false as documented.
Adds the regression suite this PR describes, on top of the shared
harness.

Covers unresolved-host diagnostics in both the Linux and macOS resolver
wordings, including that they skip interactive authentication while
other batch failures still reach it, and that authentication failures
report both exit codes. Covers SSHFS failure reporting: the exit code
is preserved, stderr wins over stdout, whitespace-only output is not
treated as real output, and a silent failure still reports its code.

Covers mount directory ownership from get_or_create, including the
creation race and the non-raising failure path, and the cleanup rules
after a failed attempt: only a directory this attempt created and left
empty is removed, never one that pre-existed, holds contents, or turns
out to be an active mount. Also covers that a cleanup error does not
mask the connection error and that a failed attempt is never registered
in the lockfile.

Refs #24
ControlMaster sockets are Unix domain sockets, so their paths are capped
by sun_path: 108 bytes on Linux, 104 on macOS and the BSDs. SSH appends
"/%C" plus a temporary suffix while creating one, leaving 49 characters
for connections.socket_dir on Linux and 45 on macOS.

Exceeding that makes every connection fail with "unix_listener: path
... too long for Unix domain socket", which names neither the setting
at fault nor the limit involved. Report it from :checkhealth instead: an
error past the budget, a warning within ten characters of it, and the
measured budget either way. The check runs outside the ~/.ssh branch,
since socket_dir is configurable and need not live there.
@uhs-robert
uhs-robert marked this pull request as ready for review September 10, 2026 22:56
The 58 character reserve was a bare magic number mirrored in the spec, so a
wrong guess could not be caught. The %C hash half is now checked against real
ssh -G output, and the temporary suffix is marked as an upper bound so the
budget errs short rather than passing a path that fails at connect time.
@uhs-robert
uhs-robert merged commit 8c7bf7f into main Sep 11, 2026
2 checks passed
@uhs-robert
uhs-robert deleted the fix/10-connection-failure-cleanup branch September 11, 2026 01:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants