If you’re building a daemon on macOS and your project directory is in iCloud Drive, your LaunchAgent will fail silently.
The Failure Mode
You write a .plist, drop it in ~/Library/LaunchAgents/, load it with launchctl bootstrap or launchctl load. launchctl list shows the service. Status code 0. Looks fine.
Then nothing runs. No logs. No errors. No PID. The service “loaded” but never executed anything that touches your iCloud-resident files.
The reason: launchctl runs processes in a restricted environment that cannot access ~/Library/Mobile Documents/com~apple~CloudDocs/. The cleanest reproduction:
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/Users/you/Library/Mobile Documents/com~apple~CloudDocs/Documents/daemon/main.py</string>
</array>
The plist looks valid. launchctl accepts it. macOS silently refuses to execute. No exit code surfaces in launchctl list. No ~/Library/Logs/com.you.daemon.log gets written because the binary never ran.
The Fix
Copy runtime files to a non-iCloud directory. Conventional location: ~/yourdaemon/ (just a top-level home folder). Point the plist there. Use an install script that syncs from the iCloud-resident source to the runtime location.
#!/bin/bash
SRC="/path/to/icloud/daemon"
DEST="$HOME/yourdaemon"
mkdir -p "$DEST"
cp -r "$SRC"/*.py "$DEST"/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.you.daemon.plist
Now launchctl runs out of $HOME/yourdaemon/ which it can access, and the daemon starts. Updates require re-running the install script.
What Else Doesn’t Work
- Symlinks from non-iCloud paths to iCloud paths.
launchctlfollows the symlink and hits the same wall. - Setting
EnvironmentVariables.HOMEto a non-iCloud path. Doesn’t help. The restriction is on file access, not on env vars. - Running the daemon as root with
sudo launchctl. Still blocked. The TCC system enforces it across users.
Related
If your daemon also reads or writes files inside the iCloud workspace, that’s a separate problem. launchctl-spawned processes can write to non-iCloud paths fine, but writing INTO ~/Library/Mobile Documents/ requires Full Disk Access (TCC) granted to the actual binary the plist runs, not the symlinked or shimmed one. See the convergent-logging field note for the four failed attempts I went through to get that working.