Summary
fuse-t is the actively-maintained, kextless
replacement for macFUSE (no kernel extension, works on Apple Silicon without reduced-security
boot). sshfs.nvim advertises macOS support but has no fuse-t handling, and two independent
defaults make it unusable under fuse-t out of the box.
Environment
- sshfs.nvim
v2.0.0-42-g57f5862
- macOS; fuse-t cask
1.2.7 + fuse-t-sshfs cask 1.0.2 (from the
macos-fuse-t/homebrew-cask tap; upstream macos-fuse-t/sshfs,
a fuse-t-specific fork reporting SSHFS 2.9 / libfuse 2.9.9 via sshfs --version)
- No macFUSE kext, and no generic Homebrew
sshfs formula installed
Bug 1 — mount detection misses fuse-t (breaks keymaps + auto-unmount)
fuse-t presents an SSHFS mount as an NFS mount:
fuse-t:/sshfs_nvim_mount on /Users/me/mnt/host (nfs, nodev, nosuid, mounted by me)
get_system_mounts() (lua/sshfs/lib/mount_point.lua:35-43) only matches fuse.sshfs,
(macfuse, (osxfuse, and the scan loop only proceeds on lines matching sshfs/macfuse.
The fuse-t line matches none → list_active() returns [] even with a live mount.
What happens:
- Every mount-dependent keymap fails with the notify
No active SSH connections.
cleanup_unused_mounts() on VimLeave (init.lua:55 → session.lua:156) iterates
list_active(), so the mount is never unmounted on exit → orphaned fuse-t mount +
stale mountpoint.
Expected: fuse-t mounts are detected like macFUSE / fuse.sshfs — keymaps see the active
mount and it is unmounted on exit.
Bug 2 — default cache options hang under sshfs 2.9
config.lua:18-20 defaults:
dir_cache = "yes",
dcache_timeout = 300,
dcache_max_size = 10000,
build_sshfs_args (lib/sshfs.lua:9-24) turns these into dir_cache=yes, dcache_timeout=300,
dcache_max_size=10000, passed as a single comma-joined -o (lib/sshfs.lua:64:
{ "sshfs", remote, mount, "-o", table.concat(options, ",") }).
What happens: dir_cache / dcache_* are sshfs-3.x option names (the directory-cache
rework in sshfs 3). Generic sshfs on Linux/macFUSE is 3.x and has them — but fuse-t-sshfs
is the sshfs 2.9 fork, whose sshfs --help lists only cache=BOOL, cache_timeout=N,
cache_max_size=N (no dir_cache/dcache_*). So the stock config feeds this binary option
names it doesn't define; because they share one -o, a single unknown token poisons the whole
list. Observed: the mount never completes — :SSHFSMount / <leader>mm "does nothing", no
error surfaced. Reproduces with stock config, no overrides.
Expected: stock config mounts under fuse-t-sshfs (sshfs 2.9) — the plugin emits
2.9-compatible cache=/cache_timeout= when an sshfs < 3 is detected, or documents that
fuse-t users must override these.
Secondary — unmount timing
Even with Bug 1 fixed, MountPoint.unmount (mount_point.lua:212-232) runs
jobstart+jobwait(…,5000) from a VimLeave callback; async jobs during editor teardown
are unreliable for fuse-t's NFS backend. A synchronous unmount from VimLeavePre, escalating
to diskutil unmount force, is reliable in practice.
Reproduction
- macOS with fuse-t + fuse-t-sshfs (
brew install --cask fuse-t macos-fuse-t/homebrew-cask/fuse-t-sshfs) — i.e. the sshfs 2.9 fuse-t fork, no macFUSE.
- Stock sshfs.nvim config.
:SSHFSMount a host → mount never completes (Bug 2). Set sshfs_options.dir_cache / dcache_timeout / dcache_max_size = false to get past it.
- Mount succeeds;
mount shows the fuse-t:/… (nfs) line, but any mount keymap reports "No active SSH connections" (Bug 1).
- Quit nvim → mount stays (Bug 1 cascade).
Suggested fixes
- Detection: match fuse-t NFS mounts (
fuse-t: source / (nfs type). More robust:
track plugin-created mounts in internal state (you already ship lib/lockfile.lua) instead
of scraping the system mount table, which sidesteps the format problem entirely. Note: the
fuse-t NFS line shows fuse-t:/<volname>, not user@host:/path, so host/remote-path can't
be recovered by parsing alone — internal tracking is the clean path.
- Cache options: gate
dir_cache/dcache_* behind an sshfs-major-version check, or emit
2.9-compatible cache=/cache_timeout= when sshfs < 3 is detected. At minimum, document
that fuse-t users must disable them.
- Unmount: prefer VimLeavePre + synchronous unmount; once Bug 1 is fixed the cascade
resolves.
Working reference
A complete local compat shim (detection monkey-patch, 2.9-safe option set, synchronous
VimLeavePre unmount):
sshfs-nvim.lua.
Summary
fuse-t is the actively-maintained, kextless
replacement for macFUSE (no kernel extension, works on Apple Silicon without reduced-security
boot). sshfs.nvim advertises macOS support but has no fuse-t handling, and two independent
defaults make it unusable under fuse-t out of the box.
Environment
v2.0.0-42-g57f58621.2.7+ fuse-t-sshfs cask1.0.2(from themacos-fuse-t/homebrew-casktap; upstreammacos-fuse-t/sshfs,a fuse-t-specific fork reporting SSHFS 2.9 / libfuse 2.9.9 via
sshfs --version)sshfsformula installedBug 1 — mount detection misses fuse-t (breaks keymaps + auto-unmount)
fuse-t presents an SSHFS mount as an NFS mount:
get_system_mounts()(lua/sshfs/lib/mount_point.lua:35-43) only matchesfuse.sshfs,(macfuse,(osxfuse, and the scan loop only proceeds on lines matchingsshfs/macfuse.The fuse-t line matches none →
list_active()returns[]even with a live mount.What happens:
No active SSH connections.cleanup_unused_mounts()onVimLeave(init.lua:55→session.lua:156) iterateslist_active(), so the mount is never unmounted on exit → orphaned fuse-t mount +stale mountpoint.
Expected: fuse-t mounts are detected like macFUSE /
fuse.sshfs— keymaps see the activemount and it is unmounted on exit.
Bug 2 — default cache options hang under sshfs 2.9
config.lua:18-20defaults:build_sshfs_args(lib/sshfs.lua:9-24) turns these intodir_cache=yes,dcache_timeout=300,dcache_max_size=10000, passed as a single comma-joined-o(lib/sshfs.lua:64:{ "sshfs", remote, mount, "-o", table.concat(options, ",") }).What happens:
dir_cache/dcache_*are sshfs-3.x option names (the directory-cacherework in sshfs 3). Generic
sshfson Linux/macFUSE is 3.x and has them — but fuse-t-sshfsis the sshfs 2.9 fork, whose
sshfs --helplists onlycache=BOOL,cache_timeout=N,cache_max_size=N(nodir_cache/dcache_*). So the stock config feeds this binary optionnames it doesn't define; because they share one
-o, a single unknown token poisons the wholelist. Observed: the mount never completes —
:SSHFSMount/<leader>mm"does nothing", noerror surfaced. Reproduces with stock config, no overrides.
Expected: stock config mounts under fuse-t-sshfs (sshfs 2.9) — the plugin emits
2.9-compatible
cache=/cache_timeout=when an sshfs < 3 is detected, or documents thatfuse-t users must override these.
Secondary — unmount timing
Even with Bug 1 fixed,
MountPoint.unmount(mount_point.lua:212-232) runsjobstart+jobwait(…,5000)from a VimLeave callback; async jobs during editor teardownare unreliable for fuse-t's NFS backend. A synchronous unmount from VimLeavePre, escalating
to
diskutil unmount force, is reliable in practice.Reproduction
brew install --cask fuse-t macos-fuse-t/homebrew-cask/fuse-t-sshfs) — i.e. the sshfs 2.9 fuse-t fork, no macFUSE.:SSHFSMounta host → mount never completes (Bug 2). Setsshfs_options.dir_cache / dcache_timeout / dcache_max_size = falseto get past it.mountshows thefuse-t:/… (nfs)line, but any mount keymap reports "No active SSH connections" (Bug 1).Suggested fixes
fuse-t:source /(nfstype). More robust:track plugin-created mounts in internal state (you already ship
lib/lockfile.lua) insteadof scraping the system
mounttable, which sidesteps the format problem entirely. Note: thefuse-t NFS line shows
fuse-t:/<volname>, notuser@host:/path, so host/remote-path can'tbe recovered by parsing alone — internal tracking is the clean path.
dir_cache/dcache_*behind an sshfs-major-version check, or emit2.9-compatible
cache=/cache_timeout=when sshfs < 3 is detected. At minimum, documentthat fuse-t users must disable them.
resolves.
Working reference
A complete local compat shim (detection monkey-patch, 2.9-safe option set, synchronous
VimLeavePre unmount):
sshfs-nvim.lua.