upd#2:
After spending a full day testing every possible approach, here's my findings and what actually works.
The problem
Black screen flash when exiting fullscreen apps on macOS Tahoe. Triggered by dynamic wallpapers + Reduce Motion enabled. Disabling dynamic wallpapers fixes the black screen — but then you lose the rotating wallpaper experience.
What I tried (and why it failed)
1. launchd + osascript (System Events)
Script ran, log was written, but wallpaper never changed visually.
Reason: FSFindFolder error -43 — System Events can't access GUI context from a background process.
2. cron + osascript (Finder)
Worked manually, but not on schedule.
Reason: cron on macOS Tahoe throws Could not switch to audit session: Operation not permitted — Apple blocks audit session switching for background processes.
3. launchd + osascript (Finder)
Same issue as above.
4. defaults write com.apple.desktop
Wallpaper was written to the plist correctly, but macOS Tahoe ignores com.apple.desktop — it now uses the com.apple.wallpaper store.
5. cron + Swift (NSWorkspace.setDesktopImageURL)
Worked manually, unstable on schedule — same audit session problem.
6. launchd + Swift + SessionCreate
Partially worked — sometimes SkyLight got a proper GUI session, sometimes not. Completely unpredictable.
What actually works: Hammerspoon
After all the above failed, I switched to Hammerspoon — a free, open-source macOS automation tool. It runs as a proper GUI app in the menu bar, always in the correct audit session, so it has no permission issues whatsoever.
Install:
bash
brew install --cask hammerspoon
Config (~/.hammerspoon/init.lua):
lua
local wallpaperDir = "/Users/YOUR_USERNAME/Pictures/wallpapers"
local function setRandomWallpaper()
local files = {}
local handle = io.popen('find "' .. wallpaperDir .. '" -maxdepth 1 -type f \\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.heic" \\)')
for file in handle:lines() do
table.insert(files, file)
end
handle:close()
if #files == 0 then return end
local randomFile = files[math.random(#files)]
for _, screen in ipairs(hs.screen.allScreens()) do
screen:desktopImageURL("file://" .. randomFile)
end
print("Wallpaper set: " .. randomFile)
end
-- Set wallpaper immediately on launch
setRandomWallpaper()
-- Then every 15 minutes
hs.timer.doEvery(900, setRandomWallpaper)
Replace YOUR_USERNAME with your actual username (check with whoami in Terminal).
After saving the config, click Reload Config in the Hammerspoon menu bar icon.
Add Hammerspoon to System Settings → General → Login Items so it starts automatically on boot.
The root cause of why all shell-based approaches fail is Could not switch to audit session: Operation not permitted — macOS Tahoe intentionally blocks background processes from accessing the display session. Hammerspoon bypasses this because it's a proper GUI application.
Hopefully Apple fixes the original black screen bug so none of this is necessary 🙃