Schedule with launchd
Control environment and output
Give a scheduled job absolute paths, a working directory, and dedicated output files instead of depending on Terminal configuration.
10 minute lesson
A LaunchAgent does not inherit your interactive .zshrc environment. Commands that work in Terminal can fail because PATH, working directory, or variables differ. This single fact explains most “works when I run it, fails on the schedule” reports.
The PATH a launchd job receives is short:
/usr/bin:/bin:/usr/sbin:/sbin
No /opt/homebrew/bin, no ~/bin. A script that calls jq or anything else Homebrew-installed dies with command not found. Call tools by absolute path (/opt/homebrew/bin/jq), or set PATH explicitly at the top of the script, where it lives in version control next to the code that depends on it.
The working directory is not your project folder either. Configure WorkingDirectory, StandardOutPath, and StandardErrorPath when useful:
<key>WorkingDirectory</key>
<string>/Users/flavio</string>
<key>StandardOutPath</key>
<string>/Users/flavio/Library/Logs/screenshot-sorter.log</string>
<key>StandardErrorPath</key>
<string>/Users/flavio/Library/Logs/screenshot-sorter.err.log</string>
The two output paths are your only window into a job that runs with no terminal attached: launchd appends everything the job prints to those files. One caveat — launchd does not build the directory tree for you. Create parent directories before loading the job, or the output silently goes nowhere.
When a job misbehaves and you suspect the environment, make the job show you what it sees:
/usr/bin/env > /Users/flavio/Library/Logs/sorter-env.txt
Put that line at the top of the script, run the job once, and compare the dump with env in your terminal. The difference is usually the whole answer.
Keep secrets out of the property list and logs. A plist is a plain file on disk, and log files get zipped into bug reports. Retrieve secrets at runtime through a protected mechanism — the user Keychain via security find-generic-password works for agents, since they run in your session — and avoid shell tracing: set -x prints every expanded command, secrets included, straight into the error log.
Lesson completed