LaunchMon

LAUNCHD FIELD NOTES

launchctl “Load failed: 119: Service is disabled”: how to enable a disabled launchd service

Error 119 means the service has a persistent disable override in that launchd domain, kept outside the plist. Find it with launchctl print-disabled, clear it with launchctl enable, then bootstrap the job again.

The plist is valid, the executable exists, and launchctl still refuses to load it:

Load failed: 119: Service is disabled

bootstrap reports the same number as Bootstrap failed: 119: Service is disabled, and launchctl error 119 decodes it to the same words. Nothing in the file is wrong. launchd keeps a list, per domain, of services that have been switched off, and your Label is on it. Deleting and recreating the plist does not help, because the list is not in the plist.

Start in Terminal

These examples use a placeholder label. Substitute the exact Label from your own plist, not its filename. First confirm the override exists in the domain you are loading into:

plutil -extract Label raw ~/Library/LaunchAgents/local.example.plist
launchctl print-disabled "gui/$(id -u)" | grep -i example

Without the grep, the output is a dictionary of every service in the domain that has an explicit override, one way or the other:

	disabled services = {
		"local.example" => disabled
		"homebrew.mxcl.redis" => enabled
	}

A line that says disabled is your answer. Clear it with enable, then load the job the normal way. Both print nothing on success:

launchctl enable "gui/$(id -u)/local.example"
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/local.example.plist
launchctl print "gui/$(id -u)/local.example" | grep -E "state|last exit"

For a LaunchDaemon the list belongs to the system domain. Reading it does not need root; changing it does:

launchctl print-disabled system | grep -i example
sudo launchctl enable system/local.example
sudo launchctl bootstrap system /Library/LaunchDaemons/local.example.plist

What to check next

Where the list lives: /var/db/com.apple.xpc.launchd/disabled.plist for the system domain and disabled.<uid>.plist for each user. The files are owned by root and meant for launchd alone. Read them if you are curious, but change them with launchctl.

Why it happens

Something, at some point, asked launchd to keep this service off. The usual sources are launchctl disable; the legacy launchctl unload -w, whose -w flag, in the manual’s words, “overrides the Disabled key and sets it to true”; an app’s uninstaller or a device-management agent cleaning up; or a deliberate stop-and-disable that was never reversed. The override persists across reboots, and because launchd stores it rather than the plist, it also survives reinstalling the app or recreating the file. That is why a freshly reinstalled service can fail with 119 on its first load.

Older versions of macOS wrote the Disabled key back into the plist itself. The launchd.plist manual notes that this state is now kept externally, which is precisely what makes it invisible when you read the file. If you want a job to stay off, this is still the right tool: bootout stops it until the next login or boot, disable keeps it from loading at all. To remove it for good, boot it out, then delete the plist.

User agents usually run in gui/<your uid>; system daemons run in system. The same label in different domains refers to different service registrations.

See it in LaunchMon

LaunchMon reads print-disabled for each domain and shows the result as the Override row in a job’s live state: Default, Enabled or Disabled. Enable and Disable are actions with a confirmation that says what will change, so a job that will not load has its reason on screen next to its plist and logs.

LaunchMon’s detail pane for a service, showing its domain, PID, last exit, launch count and Override row.
LaunchMon with fictional demo services.

Download LaunchMon free trial

Related guides

References: Apple: creating launchd jobs. For commands on your macOS version, run man launchctl and man launchd.plist.