"Life is all about sharing. If we are good at something, pass it on." - Mary Berry

launchctl: Bootstrap failed: 5: Input/output error

2025-09-26

Categories: Programming

Initial symtoms

We have an internal service that needs to start a boot. The responsible team wrote a simple launchd script, like this:

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3<plist version="1.0">
 4<dict>
 5    <key>Label</key>
 6    <string>com.example.watchdog</string>
 7
 8    <key>ProgramArguments</key>
 9    <array>
10        <string>/usr/local/bin/watchdog</string>
11    </array>
12
13    <key>RunAtLoad</key>
14    <true/>
15
16    <key>KeepAlive</key>
17    <true/>
18
19    <key>SessionCreate</key>
20    <true/>
21
22    <key>StandardOutPath</key>
23    <string>/var/log/watchdog.out</string>
24
25    <key>StandardErrorPath</key>
26    <string>/var/log/watchdog.err</string>
27</dict>
28</plist>

However, when trying to load it, we ran into this error:

1sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.watchdog.plist
2Password:
3Bootstrap failed: 5: Input/output error

Interestingly, the same error also appears if the service is already running. But in this case, it wasn’t:

1sudo launchctl bootout system /Library/LaunchDaemons/com.example.watchdog.plist
2Boot-out failed: 5: Input/output error

To rule out issues with the binary itself, I tried starting it manually:

1/usr/local/bin/watchdog

It ran without problems, so the error cleary happens before the binary is executed.

Unfortunately, man launchctl shows no option to enable debugging for bootstrap, which makes troubleshooting trickier.

Narrowing down the cause

I then tried swapping the binary path in a known working launchd script with /usr/local/bin/watchdog. The modified script looked like this:

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3<plist version="1.0">
 4<dict>
 5        <key>Crashed</key>
 6        <true/>
 7        <key>KeepAlive</key>
 8        <true/>
 9        <key>Label</key>
10        <string>org.openvpn.client</string>
11        <key>ProgramArguments</key>
12        <array>
13                <string>/usr/local/bin/watchdog</string>
14        </array>
15        <key>RunAtLoad</key>
16        <true/>
17        <key>SessionCreate</key>
18        <true/>
19        <key>StandardOutPath</key>
20        <string>/var/log/ovpnagent.log</string>
21</dict>
22</plist>

By removing keys one by one, I eventually found the culprit: the label value. If the label is set to com.example.watchdog, the bootstrap failed. If I changed it to something else, it worked.

Root cause

It turned out the service had been accidentally disabled during earlier experiments with the .plist. Running:

1sudo launchctl print system

revealed this:

1disabled services = {
2        "com.example.watchdog" => disabled
3}

Fix

The solution is to re-enable the service and then bootstrap it again:

1sudo launchctl enable system/com.example.watchdog
2sudo launchctl bootstrap system /Library/LaunchDaemons/com.example.watchdog.plist

Tags: macOS launchd

Edit on GitHub

Related Posts: