Repository navigation
macOS: reconnect fails with [Errno 2] after ~3 days, because dirhelper deletes the bundled CA file #175
Description
Summary
On macOS the client builds its WebSocket SSL context from certifi.where(). In the one-file
PyInstaller bundle that path resolves inside the extraction directory,
/var/folders/<...>/T/_MEI<random>/certifi/cacert.pem. /usr/libexec/dirhelper, run by
/System/Library/LaunchDaemons/com.apple.bsd.dirhelper.plist at 03:35 daily with
CLEAN_FILES_OLDER_THAN_DAYS=3, deletes files under /var/folders/.../T/ that have not been
accessed in three days — including the contents of an _MEI directory belonging to a
still-running process. Apple documents the behaviour on _CS_DARWIN_USER_TEMP_DIR: files
there "may be cleaned (removed) by the system if they are not accessed in 3 days."
certifi.where() is only called on the (re)connect path, so a long-lived client reads that file
for the first time in days exactly when it needs to reconnect, and by then it is gone:
ERROR - Connection to remote host was lost. - goodbye
ERROR - Failed to connect websocket: [Errno 2] No such file or directory
Combined with the single-attempt reconnect (#174), this ends clipboard sync
permanently, with the process still running and the tray icon unchanged.
Where
utils/ssl_helper.py (3.2.0):
if PLATFORM == MACOS:
ctx = ssl.create_default_context(cafile=certifi.where())
return {"context": ctx}The Linux and Windows paths return None and use the library/OS default, so they are unaffected.
Reproduce
Leave the macOS app running for more than three days without a reconnect, then break and restore
the connection. The evidence is visible without waiting, on any machine where the app has been
up that long:
$ for d in /var/folders/*/*/T/_MEI*; do
printf '%s mtime=%s cacert=%s\n' "$d" "$(stat -f '%Sm' "$d")" \
"$(test -f "$d/certifi/cacert.pem" && echo present || echo MISSING)"
done
/var/folders/…/T/_MEIB8SvLX mtime=Sep 8 03:35:04 2026 cacert=MISSING
/var/folders/…/T/_MEIiqFfIl mtime=Sep 2 03:35:04 2026 cacert=MISSING
/var/folders/…/T/_MEIMvyX83 mtime=Sep 10 03:35:28 2026 cacert=MISSINGThe ~03:35 mtimes are the daily purge. The app in question had started 3.2 days before its
reconnect failed.
Suggested fix
Any of these removes the failure; the first is the smallest:
- Resolve and read the CA bundle once at startup — e.g. load it into an
SSLContextin
__init__and reuse that context — so the reconnect does not depend on a file that may have
been deleted since. This does not survive a purge plus a context rebuild, but it removes
the common case. - Copy the bundle out of
sys._MEIPASSinto a directory macOS does not purge (e.g. under
~/Library/Application Support/ClipCascade/) on first run, and pointcafilethere. - Fall back to the platform default when the file is gone:
if PLATFORM == MACOS:
path = certifi.where()
ctx = ssl.create_default_context(cafile=path) if os.path.isfile(path) \
else ssl.create_default_context()
return {"context": ctx}Worth noting for whichever is chosen: the same reasoning applies to any other data file the
bundle reads lazily rather than at startup.
Environment
- ClipCascade 3.2.0, macOS build (
ClipCascade-Apple_macOS.ARM_M-Series.zip), Apple silicon,
launched at login and left running for days. - Server 0.7.0, self-hosted, reached over
wss://through a reverse proxy with a public CA
certificate (nossl_ca_bundleconfigured, so thecertifibranch is the one taken).
Activity
Sathvik-Rao commentedon Oct 25, 2026
#182
build(macos): package the app as onedir instead of onefile