launchctl load alongside log show to diagnose specific error messages returned by the system daemon.
Verifying File Location and Ownership
The most common cause for a LaunchAgent not starting is an incorrect directory placement. macOS distinguishes between system-wide daemons and per-user agents. Your custom scripts must reside in ~/Library/LaunchAgents/. If you place them in /Library/LaunchAgents/, they may require root privileges or specific configuration that standard users do not possess. Similarly, placing them in /System/Library/LaunchAgents/ is reserved for Apple’s core services and should never be modified.
Once the file is in the correct directory, you must ensure the ownership is correct. The file must be owned by the user who is supposed to run the agent. You can check this using the ls -l command in Terminal. The output should show your username as the owner. If the file is owned by root or another user, the launchd daemon will ignore it for security reasons. Use the chown command to fix ownership issues:
chown $USER:staff ~/Library/LaunchAgents/com.example.myagent.plist
Additionally, verify the file permissions. The plist file itself should have read permissions for the user and group, typically set to 644. The containing directory ~/Library/LaunchAgents/ should also be accessible. Incorrect permissions are a frequent source of silent failures where no error message is logged because the daemon cannot even read the file to parse it.
Validating XML Syntax and Structure
LaunchAgents are written in XML. Even a single misplaced character can prevent the entire agent from loading. Common syntax errors include unclosed tags, missing quotation marks around string values, or incorrect data types for keys like KeepAlive or RunAtLoad. These keys expect boolean values (true or false), not strings. Using quotes around booleans is a frequent mistake that causes immediate failure.
To validate your plist, use the plutil command. This tool checks the XML structure and reports specific line numbers where errors occur. Run the following command in Terminal, replacing the filename with your actual plist name:
plutil -lint ~/Library/LaunchAgents/com.example.myagent.plist
If the output says "The operation couldn’t be completed," there is a structural error. Fix the reported issue and re-run the lint command until it returns success. After fixing syntax, always restart the launchd process or log out and back in to ensure the new XML is parsed correctly. Refer to our guide on Resolving 'atrun' Issues on macOS: Launchd Not Working, Disappearing Queues for deeper insights into how launchd handles queue processing failures.
Diagnosing Runtime Errors with Logs
When a plist loads but the script fails to execute or crashes immediately, you need to inspect the system logs. macOS stores detailed information about launchd events in the Unified Logging System. You can query these logs using the log command in Terminal. Filter the results by the label you defined in your plist file under the Label key.
log show --predicate 'process == "launchd" AND subsystem == "com.apple.launchd"' --info | grep com.example.myagent
This command filters the log stream for entries related to your specific agent. Look for lines indicating "Exited with code" or "Failed to start." If your script relies on environment variables such as $PATH or $HOME, note that LaunchAgents do not inherit the interactive shell environment. You must explicitly define these variables within the plist using the
Another critical factor is the working directory. By default, launchd sets the working directory to / (the root directory). If your script expects to run relative to its own location or the user's home folder, it will fail. Add the $HOME or the absolute path to your script's directory.
Reloading and Testing the Agent
After making changes to your plist, you need to reload it without rebooting. Use the launchctl command to unload and then load the agent. First, unload the existing instance to clear any cached state:
launchctl unload ~/Library/LaunchAgents/com.example.myagent.plist
Then, load it again:
launchctl load ~/Library/LaunchAgents/com.example.myagent.plist
If you encounter permission denied errors during load, double-check the ownership and permissions discussed earlier. You can also check if the agent is currently loaded by running launchctl list and grepping for your label. A successful load will show a PID number or a status indicating it is active. For more complex troubleshooting involving terminal crashes that might affect diagnostic tools, see How to Resolve Unexplained 'crash' Entry in MacOS 15.1 Terminal.
Frequently Asked Questions
Why does my LaunchAgent work after reboot but not after login?
This usually happens when the agent depends on network services or external drives that are not yet mounted at login time. Ensure you set
Can I debug a LaunchAgent script directly?
Yes, you can redirect stdout and stderr to a file within your plist using
What is the difference between LaunchAgent and LaunchDaemon?
LaunchAgents run in the context of a specific user session and only execute when that user is logged in. LaunchDaemons run at the system level before any user logs in and require root privileges. Misplacing a daemon-style script in the Agents folder will prevent it from running entirely.
How do I remove a stuck LaunchAgent?
If an agent is stuck in a failed state, first try unloading it with launchctl unload. If that fails, you may need to kill the associated process using killall or pkill. Finally, delete the plist file and clear the launchd cache by removing files in ~/Library/Caches/com.apple.LaunchServices if necessary.
Does macOS SIP affect LaunchAgents?
Sysadmin Integrity Protection (SIP) primarily restricts modifications to system directories. It does not generally block user-level LaunchAgents in ~/Library/LaunchAgents. However, if your script attempts to access protected system resources or modify system files, it will be blocked regardless of your plist configuration.