Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesstack72

Relationships

#3007 macOS autoupdate LaunchAgent fails with EX_CONFIG after self-update; doctor install still reports HEALTHY

Opened by dmc · 10/4/2026· Shipped 10/5/2026

Summary

On macOS, the autoupdate LaunchAgent stopped working after it replaced the binary on 2026-09-04. For a month, every daily run failed with launchd exit code 78 (EX_CONFIG). Swamp never started, so nothing was written to the autoupdate log or to stderr. swamp doctor install still reports HEALTHY, so nothing told the user that autoupdate was broken. I found it when I noticed I was a month behind (20260904 → 20261003) and updated by hand.

Environment

  • macOS 26 (Darwin 25.6.0), Apple Silicon
  • swamp 20260904.171927.0 when the failures started; still reproduces on 20261003.192630.0
  • Scheduler: LaunchAgent (user), ~/Library/LaunchAgents/club.swamp.autoupdate.plist, StartInterval 86400, RunAtLoad true
  • Binary: ~/.local/bin/swamp, ad-hoc signed (codesign -dv → Identifier=a.out, Signature=adhoc, no TeamIdentifier)

Evidence

~/.swamp/log/autoupdate.log: the last entry written by the scheduler is the update that replaced the binary:

{"timestamp":"2026-09-01T12:00:47.497Z","versionBefore":"20260725.011748.0-...","versionAfter":"20260901.013347.0-...","outcome":"updated"}
{"timestamp":"2026-09-04T17:53:58.961Z","versionBefore":"20260901.013347.0-...","versionAfter":"20260904.171927.0-...","outcome":"updated"}

No entries after that, not even up_to_date. runBackgroundUpdate appends an entry for every outcome, so the process never got that far.

launchctl print gui/<uid>/club.swamp.autoupdate:

state = not running
runs = 27
last exit code = 78: EX_CONFIG
run interval = 86400 seconds
properties = runatload | inferred program | needs LWCR update | managed LWCR | has LWCR
  • ~/Library/Logs/swamp/autoupdate.stderr.log is empty (0 bytes). autoupdate.stdout.log was last written 2026-09-04.
  • sfltool dumpbtm shows the item as legacy agent, Disposition [enabled, allowed, notified].
  • Swamp has no code path that exits 78. With HOME unset it exits 1 with an FTL message on stderr, so this is not an environment problem inside swamp. launchd fails before the binary runs.

Reproduction (on 20261003, after a manual swamp update)

  1. launchctl kickstart -p gui/$(id -u)/club.swamp.autoupdate → launchd returns a PID.
  2. launchctl print ... → runs goes up by one, last exit code = 78: EX_CONFIG. No log entry, empty stderr.
  3. ~/.local/bin/swamp update --background; echo $? run directly → exit 0, and an up_to_date entry is appended.
  4. env -i HOME=$HOME PATH=/usr/bin:/bin:/usr/sbin:/sbin swamp update --background from / → exit 0 (launchd's environment is not the cause).

Suspected cause (not confirmed)

launchd tracks a lightweight code requirement (LWCR) for the agent's executable (managed LWCR | has LWCR | needs LWCR update). The binary is ad-hoc signed, so each build has a new cdhash. Self-replacing the binary that a LaunchAgent points at seems to leave launchd with a stale requirement, and launchd refuses to spawn it (EX_CONFIG). I could not read the unified log from my session to confirm this. One thing does not fit: the 09-01 → 09-04 replacement did not break the 09-04 run. A macOS update in that window may have started enforcing this.

Possible fixes: after a successful background update, re-bootstrap the agent (launchctl bootout + bootstrap); point the plist at a stable wrapper or a properly signed binary; or sign release binaries with a Developer ID.

Second problem: the failure is silent

  • swamp doctor install reports Scheduler: installed, Last check: <timestamp> (up to date), HEALTHY. "Last check" comes from the newest autoupdate log entry, whatever wrote it. My manual run wrote one, so doctor looked healthy. Before that it would have shown a month-old timestamp, but still not flagged it.
  • Expected: doctor (and possibly the update-available banner) should flag it when the last scheduled check is older than about 2× the cadence, and should report the scheduler's last exit code from launchctl print when it is not 0.

Related: #1414 (launchd EX_CONFIG for worker daemon while daemon status reported running).

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 8 MOREREVIEW+ 48 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

10/5/2026, 9:53:07 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack7210/5/2026, 5:20:50 PM
Editable. Press Enter to edit.

dmc commented 10/4/2026, 12:34:03 PM

Workaround confirmed, and it supports the LWCR theory.

Reloading the agent fixed it:

launchctl bootout gui/<uid>/club.swamp.autoupdate
launchctl bootstrap gui/<uid> ~/Library/LaunchAgents/club.swamp.autoupdate.plist

Before the reload (27 runs, all exit 78):

last exit code = 78: EX_CONFIG
properties = runatload | inferred program | needs LWCR update | managed LWCR | has LWCR

After the reload, the RunAtLoad run and one launchctl kickstart both exited 0 and appended up_to_date entries to ~/.swamp/log/autoupdate.log:

runs = 2
last exit code = 0
properties = runatload | inferred program

The LWCR flags are gone after the re-bootstrap. That fits launchd holding a code requirement for an older ad-hoc-signed binary and refusing to spawn the self-replaced one. I expect the failure to return after the next auto-update replaces the binary. I will add a comment if it does.

stack72 commented 10/5/2026, 9:53:11 PM

Thanks @dmc for reporting this! We shipped: Re-register the macOS autoupdate launchd job after every binary update so launchd drops the code requirement pinned to the old ad-hoc cdhash (foreground update directly; background update via the next swamp command's post-hook, which also repairs installs already stuck on EX_CONFIG, never while a scheduled run is in flight, with failed retries throttled to once per 24h), and make a dead autoupdate job visible: doctor install flags a last check older than max(2x cadence, 72h) and a failing launchd job, and the CLI warns when autoupdate has stopped checking.. The fix has been merged and a release is on its way. We appreciate your contribution to swamp.

Sign in to post a ripple.