Build1 publisher3 min readPublished
Your script exited 0 because the file no longer existed: macOS evicted it to iCloud
An operator running 160-plus launchd jobs lost four days of publishing when macOS "Optimize Storage" turned 69 article files into dataless stubs. Nothing crashed. Nothing alerted.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- The author runs 160+ launchd jobs around the clock, and argues the execution environment itself becomes a failure source before script logic does.
- Every one of the author's publishing lanes went dark for four days, and every script involved exited with status 0; nothing had crashed.
- The files had quietly stopped existing on disk because macOS uploaded them to iCloud and deleted the local copies to "optimize storage".
- macOS "Optimize Storage" (System Settings > General > Storage) on a machine with iCloud Drive enabled uploads files under Desktop and Documents to iCloud and deletes the local copies when free disk space gets tight.
- In Finder the evicted files still look like normal icons but have no local data; they are in a "dataless" state, and clicking one downloads it automatically.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A developer who runs more than 160 launchd jobs around the clock reports that every one of his publishing lanes went dark for four days, and that every script involved exited with status 0 [1][2]. The cause was not his code: macOS had uploaded the input files to iCloud and deleted the local copies to "optimize storage" [3].
The mechanism is documented behaviour, not a bug. With iCloud Drive on and Optimize Storage enabled, macOS uploads files under Desktop and Documents and removes the local data when free disk space gets tight [4]. In Finder the icons look normal; there is simply no local data, a state Apple calls dataless, and clicking one triggers a download [5]. For a person at a keyboard that is a reasonable trade. For an unattended job it is not.
According to the author's post-mortem, `python3`'s `open()`, `pathlib.Path.read_text()`, `cat`, `jq` and `cp` all die immediately on a dataless file with Errno 11, `EDEADLK: Resource deadlock avoided` [6]. No lock is contended and no thread is stuck; macOS repurposes that POSIX code to mean "waiting for a file download" [7]. `EAGAIN` also appears, as a race just after a download starts [8].
The failure surfaced on August 6, 2026, when automated publishing stopped across every lane and the error log filled with EDEADLK [9]. Sixty-nine article files under `~/Desktop/Article/` had gone dataless after disk usage hit 98 percent, with 25GB free [10]. That directory was exactly what the note lane read from, so it could not read, could not post, and produced zero posts for four days starting August 3 [11].
The instructive part is the exit code. The reading script returns 0, the absence of readable files is interpreted as "no articles found, nothing to do," and the liveness check therefore records success [12]. A monitor that watches exit status sees a healthy fleet. The revenue-producing lane was stopped in a shape that showed neither errors nor failure alerts [13].
It gets worse for triage. The previous day's incident, on August 5, was a resource leak: memory that never freed and orphaned processes piling up [14]. Different root cause, identical symptom, which is "all lanes stopped" [15]. Without a written triage pattern, the author says, you burn an hour each time [16].
The remediation has two parts, and only one of them is a fix. First, get the data out of the synced tree: he moved the actual contents of 796 article files from `~/Desktop/Article/` to `~/content/article/` and left a symlink at the old path so a launchd job firing mid-migration could still read through it [17]. Path constants were updated across nine files, six JS/MJS and three shell, in commit a1b37dc [18]. Second, wrap reads: `read_text_resilient(path, attempts=4)` calls `brctl download` on EDEADLK or EAGAIN, backs off 2, 4 then 8 seconds, raises any other OSError immediately, and re-raises if the fourth attempt fails [19]. That is up to 14 seconds of waiting per file before the job legitimately gives up [20].
Worth watching if you run anything scheduled on a Mac. This is not a Desktop problem; every iCloud-synced folder is in scope, and the author found Markdown under `~/Documents/claude-obsidian/`, including `CLAUDE.md`, being re-evicted no matter how often he materialised it with `brctl download`, because the disk pressure never let up [21][22]. Materialising files is not a remedy while the volume stays full. The check to add is not "did the job exit 0" but "did the job process the number of items it should have," and the audit to run is a grep of your job definitions for any path under Desktop or Documents. Ninety-nine percent of the eviction risk here was a directory choice made long before the incident: 69 of 796 files, about 9 percent, were enough to stop the lane [23].