Troubleshooting¶
Start with sshdrive doctor. It prints one line per check, marked ok, warn, fail or
note, with a remedy under every line that is not ok. Find the check by name below; if
doctor is all green, skip to Problems with doctor green.
sshdrive doctor
sshdrive status nas # state, last error, held deletions, what the server can do
sshdrive logs --last 30m # the last half hour
sshdrive logs -f nas # one location, live
sshdrive logs shows SSH Drive's own log and the system's fileproviderd lines about your
locations. The second half is where macOS records what it asked for and what it made of the
answer.
doctor checks¶
In the order doctor prints them.
agent reachable (fail)¶
The CLI got no answer from the background agent. The detail line says which of two faults it is:
| Detail | Meaning | Fix |
|---|---|---|
| no answer on the mach service | The login item is not registered, or is switched off | open -g -a "SSH Drive". If that does not help, switch SSH Drive on in System Settings → General → Login Items & Extensions |
| the agent answered the connection but not the command in time | The agent is running but stuck, usually behind a File Provider call that has not returned | sshdrive agent restart |
The agent will not start after an upgrade¶
This is the usual cause of an agent that worked until the app was replaced. The log shows launchd retrying every 10 seconds:
Could not find and/or execute program specified by service: 3: No such process
Service could not initialize: copy_bundle_path(...), error 0x6f
The login item still looks enabled, but launchd cannot find the program, and registering it
again does nothing until it has been unregistered. The cask's postflight does this for you.
By hand:
SSHDRIVE_AGENT_ROLE=unregister "/Applications/SSH Drive.app/Contents/MacOS/SSH Drive"
open -g -a "/Applications/SSH Drive.app"
sshdrive doctor
No logout is needed. Locations, mounts, cached files and pending uploads all survive.
CLI on PATH (warn)¶
sshdrive is not in any directory on your PATH. The cask symlinks it; a bundle you
installed by hand does not. Add the symlink yourself, or use the full path:
"/Applications/SSH Drive.app/Contents/MacOS/sshdrive" doctor
app in /Applications (fail)¶
The app is somewhere else. A copy run from ~/Downloads or a build directory works for a
while and then confuses LaunchServices and the login item. Move it to /Applications, or
install with the Homebrew cask.
macOS version (fail)¶
SSH Drive needs macOS 14 or newer.
login item (fail)¶
The login item is not enabled. The detail line gives its state:
- not registered or not found: the app has never been launched from its own bundle.
Run
open -g -a "SSH Drive". - requires approval: switch SSH Drive on in System Settings → General → Login Items & Extensions. A signed, notarized install should not get here: a new user gets the item already enabled, and the "Background Items Added" notification is telling you, not asking.
app group container (fail)¶
Either the agent is unsigned or missing its application-groups entitlement, or the container
exists and is not writable. A cask install has neither problem. On a copy you built yourself,
check the signature:
codesign -d --entitlements - --xml "/Applications/SSH Drive.app"
The container is ~/Library/Group Containers/RWGDZAYBM8.org.shirls.sshdrive/ and holds
config.json, domains/<location-id>/index.sqlite, capabilities.json and pins.json.
The directory existing proves nothing: macOS creates an empty one for every installed app's
group at first login. config.json inside it is what shows SSH Drive has run.
quarantine (fail)¶
The app still carries com.apple.quarantine, the attribute macOS puts on anything from a
download. The agent runs anyway, but macOS will not register the Finder extension of a
quarantined app that has never been opened in a way a person saw, and open -g does not
count. So no mount ever appears in Finder, while the agent is reachable and the login item is
enabled.
The other signs: extension registered fails, file provider domains fails with The
application cannot be used right now, and sshdrive logs shows
getDomainsForProviderIdentifier((null)) failed: FP -2001 Underlying FP -2014
Fix:
xattr -dr com.apple.quarantine "/Applications/SSH Drive.app"
open -g -a "SSH Drive"
sshdrive doctor
This registers the extension for good. pluginkit -a on the extension looks like a fix, but
the next launch undoes it.
Removing the attribute bypasses nothing. The app is notarized with the ticket stapled to it,
and spctl --assess --type execute "/Applications/SSH Drive.app" confirms that. The cask's
postflight runs that exact check before stripping the attribute, so brew install --cask
sshdrive should not land here. A copy dragged out of the DMG by hand needs the commands
above.
Gatekeeper refuses to open the app¶
"Apple could not verify "SSH Drive" is free of malware" means your copy is not notarized: a
build from source, or a release that skipped notarization. A cask install is notarized,
checks itself in the postflight and clears the quarantine attribute, so it shows no
Gatekeeper dialog at all. To check a copy:
spctl --assess --type execute -v "/Applications/SSH Drive.app"
A shipped build says accepted / source=Notarized Developer ID.
launch services records (fail)¶
doctor reads the LaunchServices records only when extension registered would fail: a stale
record matters only while it keeps the extension unregistered, and reading them takes seconds.
With the extension registered this line says not checked: the extension is registered.
LaunchServices holds a record for another copy of SSH Drive.app, and the check names its
path. That usually means the app was opened once from the mounted DMG: the record for
/Volumes/SSH Drive/SSH Drive.app outlives the volume.
LaunchServices keys records on the bundle identifier, so the other copy's record answers every
registration of the installed one. lsd logs skipping registration of an incomplete bundle
and Registration succeeded, but did not actually register anything new; returning existing
bundle, and the extension is never registered. Drop the record the check names, then launch:
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
-u "/Volumes/SSH Drive/SSH Drive.app"
open -g -a "SSH Drive"
sshdrive doctor
The app does this itself when it finds the extension unregistered, and so does the agent on
every start, so opening the app once or sshdrive agent restart is usually enough.
extension registered (fail)¶
macOS does not know about the Finder extension (SSHDriveFileProvider.appex). Check
launch services records and quarantine first: a record for another copy of the app is
the usual cause, a quarantined app is the other, and registering the extension by hand does
not survive the next launch.
If brew install or brew upgrade printed internal error in Code Signing subsystem,
failed to scan /Applications/SSH Drive.app: -10822 from spotlight, Trace/BPT trap: 5 or
-10810 kLSUnknownErr, the cask's postflight could not reach LaunchServices and registered
nothing. Opening the app does the same work:
open -a "SSH Drive"
sshdrive doctor
To do the same by hand, rebuild the bundle's own record and launch:
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
-f -R -trusted "/Applications/SSH Drive.app"
open -g -a "SSH Drive"
pluginkit -m -A -i org.shirls.sshdrive.fileprovider -vvv
If the extension is still missing, the app is probably not where LaunchServices thinks it is.
index reader (<name>) (warn)¶
One line per mounted location, reporting how the Finder extension's direct read of that
location's data last went. ok means ready, or "last extension instance exited N s ago":
macOS stops the extension whenever it is idle, and that is the line a quiet mount ordinarily
shows. Anything else is a warning rather than a failure: the location keeps working, only more
slowly.
- "the extension has never reported its reader": open the location in Finder once. If
the line stays like this, the extension is not running; see
extension registered. closed: the agent shut the reader while it restored a damaged index, and the extension never heard that the restore had finished. Opening the location in Finder starts a fresh instance, which reads again.- any other state, shown with when it was reported and its last error: if it stays
unready, open an issue with
sshdrive logsattached.
ssh (fail)¶
/usr/bin/ssh could not be run. SSH Drive always uses the system ssh by that path, so a
Homebrew OpenSSH makes no difference to this line.
~/.ssh/config parses (fail)¶
ssh -G failed on your config, which means a Host * block or an Included file uses a
keyword Apple's OpenSSH does not know. A config written for a newer Homebrew ssh does this.
Guard the block with Match exec or remove the keyword. The detail line has the exact error.
control sockets (fail)¶
There are sshdrive-* sockets in $TMPDIR that no location owns: an agent crashed and left
its ssh connections behind. doctor only reports them. To clean up:
sshdrive agent restart
The restart closes each leftover connection, removes its socket and kills the ssh process
if it is still running.
keychain (fail)¶
The agent cannot reach the keychain. This is a signing problem, not a keychain problem: the app needs an embedded provisioning profile issued for the exact certificate it was signed with. A build from source, or one signed with a different Developer ID certificate, is in this state. A cask install never is.
Only stored passwords and key passphrases stop working; browsing, opening and saving files do not use the keychain. To replace a broken copy:
brew reinstall --cask sshdrive
Or use a key that needs no stored secret.
login shell snapshot (warn)¶
The agent reads PATH and SSH_AUTH_SOCK from a fresh login shell. That is what lets a key
agent socket exported from .zshrc, or a ProxyCommand in /opt/homebrew/bin, work. When
the snapshot fails, the agent uses launchd's environment instead and will not find either.
csh and tcsh are read with -ic rather than -l, so a PATH set only in .login is
missed. The detail line says so when that applies.
file provider domains¶
Informational: the locations macOS currently holds for SSH Drive. If sshdrive list shows a
location as mounted but it is missing here, re-add it:
sshdrive mount <name>
If this line fails with The application cannot be used right now, no location is at
fault: macOS cannot find the extension. See quarantine and extension registered.
uninstall reminder (note)¶
Always printed, never a fault. Run sshdrive remove --all before brew uninstall --cask
sshdrive, because Homebrew cannot remove File Provider domains or keychain items
(uninstalling).
Problems with doctor green¶
The sidebar entry is there but nothing lists¶
Run sshdrive status <name>. The first line is the location's state, and a last error
line follows when there is one.
| State | Meaning | What to do |
|---|---|---|
not mounted |
Saved, but not in Finder | sshdrive mount <name> |
idle (not connected) |
Mounted, and nothing has asked for it since the agent started | Open it in Finder; that connects it |
online |
Connected | - |
offline (<reason>) |
Not connected. The reason is no network, a failed attempt with the next one due in so many seconds, or reconnection stopped | For a stop, see below |
sshdrive show <name> prints the resolved ssh command and the last error too. A location
that suddenly asks for a password it had stored has a keychain problem; see
keychain.
Reconnection stops after an authentication failure, a host key that did not match, or the 60 s authentication deadline. The deadline is the usual cause of a listing that hangs and then fails: SSH Drive never waits for a person unattended, so a connection that needs a touch, a one-time code or a passphrase it has not stored is stopped (secrets and host keys).
Once you have fixed the cause (added a key to the agent, changed a password back on the server), clear the stop and try once:
sshdrive debug breaker <name> --connect
sshdrive agent restart also clears it, for every location.
To replace a stored password or passphrase, remove the location and add it again; add asks
for the secret and stores it:
sshdrive remove nas
sshdrive add nas alec@nas.local
remove asks first unless you pass -y. It refuses while uploads are pending unless you pass
--force, which discards them. The location's downloaded files go with it.
The host key changed¶
A changed host key stops the location, and sshdrive status <name> prints the ssh-keygen
-R line to run. SSH Drive never accepts a new key on its own, so connect once with your own
ssh to write it to ~/.ssh/known_hosts, then clear the stop:
ssh-keygen -R nas.local
ssh alec@nas.local # check the fingerprint, answer yes, log out
sshdrive debug breaker nas --connect
sshdrive agent restart clears the stop as well.
Local Network permission, or a server on the LAN is unreachable¶
The first time the agent connects to a server on your own network, macOS asks "Allow "SSH Drive" to find devices on local networks?". It asks once, in the app's name, and nothing can suppress it.
If you refused, servers on your LAN are unreachable while ones over the internet or a VPN still work. Turn it back on in System Settings → Privacy & Security → Local Network.
Files do not update when they change on the server¶
sshdrive status <name> names the method in use and why
(remote change detection):
| Tier | How | Delay |
|---|---|---|
| tier 0, poll | an SFTP directory listing of the watched folders | minutes |
| tier 1, sweep | one find run over ssh |
up to a minute |
| tier 2, helper | a small program on the server pushing changes | about a second |
A location drops to a lower tier when the server cannot support a higher one, and status
says which reason applies: no shell access (ForceCommand internal-sftp), no spare channel
(MaxSessions 2), an unknown architecture, or helper off.
Only some folders are watched (the root set): those holding downloaded files, pinned folders, and folders opened since the agent started, up to 256. Nothing else is checked, by design. To see the list:
sshdrive debug roots <name>
Also:
- Finder lists a folder once. Opening it again sends no request; later changes arrive only through change detection.
- busybox servers have no
find -cmin, so the sweep uses-mminand misses achmodon a file whose contents did not change.statussays so.
Many files vanished on the server but Finder still shows them¶
That is the mass-deletion guard (remote change detection). A change that removes at least half a directory and at least 20 items, or empties a non-empty root, is held rather than applied, and checked again at 5 and 30 minutes. Deletions of files with an unsaved local edit are always held.
sshdrive status nas # how many are held, and why
sshdrive accept-deletions nas # apply them
Opening a held file fails with "cannot synchronize" and leaves it in place.
A file will not evict, or the cache is not shrinking¶
- Pinned items are never evicted (pinning).
sshdrive pins <name>shows what is pinned. - An item with an upload pending is refused, with the same error as a pinned item, so the error does not tell you which.
evict --allfalls back to file by file when it meets a pinned item, and for at least a minute after--unpin-all(a single file becomes evictable 5-10 seconds after an unpin). The fallback leaves the folders themselves downloaded. That is expected (cache eviction).- The TTL counts from the last download or save, not the last read. Reading a file that is already downloaded does not keep it.
An edit made offline has not been uploaded¶
macOS holds the write and offers it again on its own schedule, and SSH Drive sends it once the connection is back. To see what macOS still has waiting:
sshdrive debug materialized <name> --pending
A symlink shows as an alias, or ln -s was refused¶
Symlinks show as native items and are never followed (symlinks).
- Finder draws every symlink as Kind "Alias" with the arrow badge, whether or not its target exists.
- A link whose target is outside the location's root is not shown at all.
- Creating such a link inside the mount is refused. There is no alert: Finder marks the item
with a sync error, and
sshdrive logshas the reason.
Nothing above helped¶
Collect the logs and open an issue at https://github.com/alecdwm/sshdrive/issues:
sshdrive logs --last 1h > /tmp/sshdrive.log
sshdrive status --json >> /tmp/sshdrive.log
sshdrive doctor --json >> /tmp/sshdrive.log
Hostnames and paths are in the log on purpose. Secrets never are, but read it before posting.