LD Manager guide · launchd
launchctl “Bootstrap failed: 5: Input/output error”: what it means and how to fix it
launchd refused to load the job, and error 5 does not say why. The usual causes, the command that confirms each one, and the fix.
You run launchctl bootstrap on a LaunchAgent or LaunchDaemon and get one line back:
Bootstrap failed: 5: Input/output error
It means launchd refused to load the job. It does not mean your disk is failing: 5 is the generic error code launchctl returns for many different refusals, so the message alone does not say which one. The older launchctl load reports the same thing as Load failed: 5: Input/output error. Here are the usual causes, the command that confirms each one, and the fix, then a shortcut with LD Manager.
In the commands below, com.example.report is the job’s Label, and its file is com.example.report.plist in ~/Library/LaunchAgents. Use your own.
First, ask the system log
launchd usually logs the real reason when it refuses a job. Run the bootstrap again, then look at the last few minutes of launchd’s messages:
log show --predicate 'subsystem == "com.apple.xpc.launchd"' --last 5m
Search the output for your Label or the plist’s file name. Messages about ownership or permissions, a service that is already loaded or disabled, or a plist launchd could not read point straight at one of the causes below. If the log names nothing, work through them in order.
Cause 1: the job is already loaded
launchd will not bootstrap a Label that is already loaded in that domain. It often happens when you edit a plist and bootstrap it again without unloading the old copy first. Check whether launchd already has it:
launchctl print gui/$(id -u)/com.example.report
If that prints a block describing the job, it is loaded. Unload it, then load the edited file:
launchctl bootout gui/$(id -u)/com.example.report
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.report.plist
For a daemon, use the system domain and sudo: sudo launchctl print system/com.example.report, then sudo launchctl bootout system/com.example.report.
The same Label in two files counts too. If another plist anywhere in the same domain uses the Label, the second one is refused. Search for it:
grep -rl 'com.example.report' ~/Library/LaunchAgents /Library/LaunchAgents /Library/LaunchDaemons
Cause 2: the job is disabled
launchd keeps its own enable and disable overrides, separate from the plist. A job disabled there (with launchctl disable, or by an app or a profile) may be refused. List the overrides for your domain:
launchctl print-disabled gui/$(id -u)
If your Label appears as disabled (=> disabled, or => true on older versions of macOS), enable it and bootstrap again:
launchctl enable gui/$(id -u)/com.example.report
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.report.plist
For a daemon: sudo launchctl print-disabled system and sudo launchctl enable system/com.example.report. The old Disabled key inside the plist is a different thing; the override wins.
Cause 3: the plist is invalid
A typo in the XML, a key with the wrong type or a missing Label can make launchd reject the file. Check the syntax:
plutil -lint ~/Library/LaunchAgents/com.example.report.plist
OK means the file parses; anything else names the line. A file that parses can still be wrong for launchd: it needs a Label string and either Program or ProgramArguments, and ProgramArguments must be an array of strings. Print the file to read it as launchd will:
plutil -p ~/Library/LaunchAgents/com.example.report.plist
Cause 4: wrong ownership or permissions
launchd refuses plists that could have been changed by someone other than their owner. Files in /Library/LaunchDaemons and /Library/LaunchAgents must belong to root, group wheel, and must not be writable by group or others. Your own agents in ~/Library/LaunchAgents should belong to you and not be writable by others. Check:
ls -l /Library/LaunchDaemons/com.example.report.plist
-rw-r--r-- 1 root wheel is what launchd expects for a daemon. If it shows your user, or a w in the group or other columns, fix it:
sudo chown root:wheel /Library/LaunchDaemons/com.example.report.plist
sudo chmod 644 /Library/LaunchDaemons/com.example.report.plist
This often happens when a plist is written in your home folder and then copied into /Library with cp rather than sudo install -o root -g wheel -m 644.
Cause 5: the wrong domain
A LaunchAgent belongs in your user’s GUI domain, gui/<uid>; a LaunchDaemon belongs in the system domain, and needs sudo. Mixing them up gets refused:
# a LaunchAgent, as yourself, no sudo
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.report.plist
# a LaunchDaemon, with sudo
sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.report.plist
Two traps. Typed at your prompt, sudo launchctl bootstrap gui/$(id -u) … still targets your domain, because your shell expands $(id -u) before sudo runs; inside a script that runs as root, id -u is 0, which is root’s domain, not yours. And the gui/<uid> domain exists only while that user is logged in to the Mac’s desktop: over SSH with nobody logged in at the screen, there is no GUI domain to load an agent into.
Cause 6: the program it points at
A missing program does not usually stop the bootstrap itself: launchd loads the job, and every start then fails, typically with last exit code 78 (EX_CONFIG). It is still worth checking while you are here, because it is the next error you will see. Find the path in Program or the first item of ProgramArguments, then:
ls -l /Users/alice/bin/report.sh
xattr -l /Users/alice/bin/report.sh
The file must exist and be executable (an x in its permissions; chmod +x adds it). A com.apple.quarantine attribute from a download can stop it from running; xattr -d com.apple.quarantine removes it, if you trust the file. And launchd does not expand ~ in paths: write /Users/alice/bin/report.sh, not ~/bin/report.sh. launchctl print gui/$(id -u)/com.example.report shows the last exit code once the job has run.
The shortcut: LD Manager
LD Manager is a free, open-source Mac app and command line tool that does these checks for you. It shows each job’s layers side by side: whether the plist is valid, whether launchd has it loaded, the enable or disable override, the Login Items switch and the last exit code. Its Doctor checks for invalid plists, missing Labels, missing, non-executable or quarantined programs, an unexpanded ~, wrong ownership or permissions, missing log folders, restart loops, jobs that are enabled but not loaded, and failed runs, and offers a fix for each.
In a terminal, ldm does the same:
ldm doctor com.example.report
ldm explain 78
ldm load com.example.report --dry-run
--dry-run prints the exact launchctl commands instead of running them, so you can see what it would do first. The error itself stays launchd’s; LD Manager reads the same state the commands above read, and puts it in one place.
Bootstrap failed: 5
What does “Bootstrap failed: 5: Input/output error” mean?
launchd refused to load the job into the domain you named. 5 is the generic I/O error code, so the message does not say why: the job may already be loaded, be disabled, have an invalid or badly owned plist, or be aimed at the wrong domain. The system log usually has the reason.
Is it the same as “Load failed: 5: Input/output error”?
Yes, in practice. launchctl load is the older command for the same operation and reports the same error code; the causes and the checks are the same.
Do I need sudo?
For a LaunchDaemon in /Library/LaunchDaemons, yes: it goes into the system domain with sudo launchctl bootstrap system <plist>. For your own LaunchAgent, no: bootstrap it into gui/$(id -u) as yourself.
The job loads now but does not run. What next?
Look at its last exit code with launchctl print gui/$(id -u)/<label>. 78 (EX_CONFIG) usually means the program is missing or not executable; a negative number is the signal that killed it, such as -15 for SIGTERM.
Use the method
Try it with LD Manager
Free, open-source Mac app and CLI for launchd: see every LaunchAgent and LaunchDaemon, learn why a job failed, and fix it with a diff and a backup.
See everything LD Manager does →