Appearance
Troubleshooting
Start with doctor
bash
safekeep doctorChecks: fswatch presence, version and the monitor for your OS (fsevents_monitor on macOS, inotify_monitor on Linux), global config and every .sync syntax, no destination inside a source (copy loop), python version, permission errors in the log from the last 24h (0.3.1), TCC + launchd plist lint (macOS), inotify watch limit + systemd user unit + linger (Linux), WSL detection (0.4.0, best-effort), leftover .safekeep.tmp.* files. Exit 1 on a fatal failure; every check described below is non-fatal.
Full Disk Access (FDA)
FDA (Full Disk Access) is the TCC permission that opens the whole disk. Without it, safekeep cannot read protected folders.
Grant it to both:
- the Python interpreter running safekeep (
/usr/bin/python3or your Homebrew python) — System Settings → Privacy & Security → Full Disk Access; fswatch, when it runs as a separate binary.
Granting it to the terminal is not enough for the launchd daemon: the job inherits the login context, not the terminal's per-session consents.
Symptoms: EACCES / Operation not permitted in the log, projects missing in auto-discovery mode.
After changing permissions, rebuild the agent — a loaded launchd job keeps its old context:
bash
launchctl bootout gui/$(id -u)/com.safekeep.agent
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.safekeep.agent.plistRecent permission errors in the log
Since 0.3.1 doctor counts permission errors in the current log (~/.local/state/safekeep/safekeep.log) from the last 24 hours — the follow-up to the incident where a volume lost its permissions and 223 copies started failing with EPERM:
text
✗ errori di permesso recenti: 223 nelle ultime 24h (ultimo: 2026-10-04 18:57:29)
— log: ~/.local/state/safekeep/safekeep.log — warning non fatale: …Matched lines carry [Errno 1] Operation not permitted or [Errno 13] Permission denied. The check is non-fatal (doctor still exits 0): it is a recent symptom, not a system state. What to do:
- what is still waiting:
safekeep sync-once --dry-run; - re-grant the permissions — the destination volume on macOS, or FDA for protected folders;
- the log path is printed with the warning.
Limits, on purpose: only the current log is read (rotations .log.1 … .log.5 are not), a line whose timestamp cannot be parsed is not counted (so the number is a minimum), and a missing log skips the check.
To follow the trend instead of looking at a single run, use safekeep doctor --json: the same data in structured form (permission_errors.count_24h and permission_errors.last) — and bin/safekeep-trend.sh already appends one line per run to ~/.local/state/safekeep/permission-trend.csv (hourly agent included), so the trend is a CSV away — see Commands.
Linux: inotify watch limit
On Linux fswatch watches trees through inotify (FSEvents on macOS). One watch per directory, capped per user:
bash
sysctl fs.inotify.max_user_watchesdoctor warns below 16384 (non-fatal): a home tree of ~25k files runs out, the extra watches are silently dropped and new events stop arriving — files already backed up stay safe, and the 24h reconcile still catches up. Raise it:
bash
sudo sysctl -w fs.inotify.max_user_watches=524288 # this boot
echo 'fs.inotify.max_user_watches=524288' \
| sudo tee /etc/sysctl.d/50-safekeep.conf # and at every bootsystemd linger
The systemd user unit starts with the login session only: without linger it runs after the first login, not at boot.
bash
loginctl enable-linger $USER # no root needed
loginctl show-user $USER -p Linger # yes / noSince 0.3.1 doctor checks linger whenever ~/.config/systemd/user/safekeep.service is installed, and warns (non-fatal) when it is off:
text
✗ linger: NON abilitato — l'agent non partirà al boot senza login
→ loginctl enable-linger $USER — warning non fataleIt reads /var/lib/systemd/linger/ directly — the same truth as loginctl, without root. No systemd at all → the check is skipped; no unit installed → it is not shown. See Agent.
Windows: WSL (best-effort)
There is no native Windows build: run safekeep inside WSL (Windows Subsystem for Linux), where it is a plain Linux program. Honesty first: not yet tested on a real WSL machine — 0.4.0 ships detection and graceful degradation only; the Linux runtime it inherits (systemd user unit, inotify, fswatch) is the 0.3.0 one, E2E-tested on real Linux.
doctor prints these lines when it detects WSL (WSL_DISTRO_NAME set, or a microsoft kernel) — all three are non-fatal:
text
✔ WSL rilevato: Ubuntu (supporto best-effort, SPEC §17)no systemd user manager (WSL1, or WSL2 with systemd off) → a warning with the fix — enable it and restart WSL:
ini# /etc/wsl.conf (from Windows: wsl --shutdown, then reopen the distro) [boot] systemd=true…or skip the agent and run the daemon by hand:
safekeep run.sources/destinations under
/mnt/…→ a drvfs note: that filesystem is slow (9p translation) and case-insensitive by default — prefer a native ext4 destination inside the distro (e.g.~/backup) over/mnt/c/....
install.sh follows the same rule: without systemd it installs no unit and exits 0 with manual-start instructions instead of failing.
Nothing is copied
Is there a
.syncin the project root? No.sync→ not followed.Does any rule match the file? It is an allow-list: no matching include → not copied. Check with:
bashsafekeep sync-once --dry-run --project ~/Code/myappIs a parent directory excluded? An excluded directory prunes its subtree.
Is
dest:set in~/.safekeep? It is mandatory.
Logs
bash
tail -f ~/.local/state/safekeep/safekeep.log # both platforms (rotating, 5 x 5 MB)
journalctl --user -u safekeep -f # Linux: what the unit printsRaise verbosity with safekeep run -v (foreground) or log_level: debug in ~/.safekeep.
Destination shows pending
The destination volume is not mounted. safekeep retries the mountpoint stat with exponential backoff (1s → 60s cap) and reconciles that project as soon as the volume is back. Check with safekeep status.
Leftover .safekeep.tmp.* files
A crash during a copy. They are removed on the next copy to that destination; doctor lists any that remain, and they are safe to delete manually.