Four Wrong Diagnoses of a Locked-Out Mac

2026-08-27Updated 2026-09-13Running It Daily

Two headless Macs went dark overnight: FileVault pre-boot, a launchd domain that never existed, a false lsof reading, and a missing screen-capture grant.

Summary

Two headless Apple Silicon workstations serving models over a private network went dark overnight and stayed dark for eleven hours. The machines were healthy the entire time. Getting from "unreachable" to "recovered without a physical trip" took four wrong diagnoses, two of which we acted on. The findings that survived: a Mac sitting at the FileVault pre-boot screen has an unmistakable network fingerprint, it can be unlocked over SSH, user LaunchAgents do not survive an unattended reboot, and a headless Mac cannot grant itself the permission it needs to let you log in and fix that.

The fingerprint of a pre-boot Mac

Start here, because it's the part that saves the most time and the part we misread twice.

A machine at the FileVault pre-boot screen is not down. It has an sshd — a tiny pre-boot one — and it answers immediately. What it does not have is the encrypted data volume, and that is where /etc/ssh and every user's authorized_keys live. So:

The pair that settles it in one step is fast publickey rejection with only password auth offered. Nothing else on a healthy Mac looks like that. We initially read the same signature as memory starvation on a thrashing box, which sent us down a completely unrelated path.

A related heuristic on our network: a TTL of 128 in the ping reply means asleep; TTL 64 with port 22 open and nothing else means pre-boot.

The escape hatch: unlock FileVault over SSH

This is the single most useful thing in this post, and it's under-documented.

Apple Silicon Macs on macOS 26 can unlock FileVault over SSH. No KVM, no physical trip, no disabling FileVault. The prerequisites, all of which we already met:

requirement note
Apple silicon
macOS 26 or later
Remote Login enabled before the reboot, obviously
wired Ethernet Wi-Fi cannot work — the Wi-Fi password lives in the encrypted keychain
a same-subnet jump host that stays up the mesh VPN is not running pre-boot, so you need a LAN address

You SSH in with the account password, not a key. The volume unlocks, the connection drops briefly while the data volume mounts, the Mac finishes booting, and it prints:

System successfully unlocked.
You may now use SSH to authenticate normally.

We researched and rejected a KVM-over-IP appliance before finding this. USB HID doesn't work during the FileVault stage on Macs anyway, so the hardware wouldn't have solved it.

One caution learned the hard way: running port sweeps against the pre-boot sshd while someone is typing the unlock password trips MaxStartups, and sshd starts dropping connections with no banner — which looks exactly like a machine dying. Stop probing while a login attempt is in flight.

Wrong diagnosis #1: "the unlock logs you in, so LaunchAgents fire"

We concluded that because unlocking the disk at the console logs that user straight in, a user LaunchAgent would fire on every boot, and therefore we didn't need root or system daemons. We built out about forty agents on that basis.

Then a box came back from a reboot with two of seven services dead:

stat -f "%Su" /dev/console       ->  root      # no GUI session
launchctl list | grep -ci <ours> ->  0         # of 40 agent plists
launchctl print gui/502          ->  125: Domain does not support specified action
launchctl bootstrap gui/502 ...  ->  Bootstrap failed: 125

FileVault was on, and SSH worked fine — the disk was unlocked. The disk can be unlocked without creating a user session. fdesetup authrestart, which is what a macOS automatic update does, unlocks for exactly one boot and logs nobody in. The machine is fully up, reachable, and the gui/$UID domain has never been created, so every user LaunchAgent silently never loads. You cannot bootstrap into that domain over SSH; failure 125 is by design.

The five services we had already moved to /Library/LaunchDaemons were the only survivors. That's not a coincidence, it's the whole lesson: user LaunchAgents are not reboot-safe on an unattended machine. System LaunchDaemons are.

(Check the user's actual UID rather than assuming 501. Ours was 502 on one box.)

Wrong diagnosis #2: a false lsof reading

Locked out of one box's GUI, we checked whether Screen Sharing was even running:

lsof -nP -iTCP:5900 -sTCP:LISTEN     # over ssh, as a normal user

Empty. We concluded the service was disabled, ran launchctl enable and bootstrap, saw the port appear, and declared victory.

All of that was wrong. lsof run as a non-root user cannot see sockets held by root's launchd, and returns empty whether or not the port is open. Screen Sharing had been enabled and listening on both machines the entire time — the disabled-services list said enabled on both, and the relevant preference file's modification time never changed when we "enabled" it, proving our command wrote nothing.

Ground truth for a launchd-held port is either sudo lsof, or better, probe from a different host: nc -z <host> 5900. Confirm reachability from outside the box before concluding anything on it is disabled.

While we're here: com.apple.screensharing showing state = not running when idle is normal. It's socket-activated. The launchd-held socket is what matters, not the daemon's state.

Wrong diagnosis #3: the Remote Management incantation

The command every search result gives you:

sudo .../ARDAgent.app/Contents/Resources/kickstart -activate -configure -access -on -restart -agent -privs -all

It prints "Activated Remote Management." and gives you no login-window VNC, because ARDAgent is itself a user agent. It cannot start without the GUI session you are trying to create. Apple documents that as of macOS 12.1, Screen Sharing cannot be enabled by kickstart at all — it only loads a job and kills agents, and never talks to the privacy daemon. Its own MSG_TRUST_ABSENT warning is it telling you this.

The system daemon com.apple.screensharing does serve the login window and does work with no session. That distinction — user agent versus system daemon — is the thing to carry away.

Wrong diagnosis #4: the missing privacy grant is the root cause

Comparing the two machines, we found exactly one difference in a SIP-protected database:

sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \
  "select service,client,auth_value from access where service='kTCCServiceScreenCapture';"

box A:  (row absent)
box B:  kTCCServiceScreenCapture | com.apple.screensharing.agent | 2

The symptom is genuinely deceptive. The VNC connection succeeds. The port probes open. The daemon runs for minutes. Every configuration check passes. The client shows "Screen Sharing is not permitted on this host. Disable and re-enable Screen Sharing or Remote Management in System Settings" — which sends you hunting for a disabled service that is not disabled. The truth is only in the log:

tccd: Service kTCCServiceScreenCapture does not allow prompting; returning denied.
      accessing = com.apple.screensharing.agent  (loginwindow, euid=0)
screensharingd: Unable to capture screen

The privacy daemon refuses to prompt because there is no GUI user to ask, and the agent is a platform binary so it cannot self-approve.

And that was still not the root cause. The real asymmetry between the two machines is physical: one has a monitor attached and one is headless.

With a real display, WindowServer owns an actual framebuffer and serves screen capture itself — no grant required, and the log shows WindowServer as the requestor. Headless, there is no framebuffer, so macOS must synthesize a virtual display through the screen-capture framework, which routes through a different system process and does require the grant. The grant can only be written by a GUI user in System Settings or by an MDM command — neither of which is reachable on a headless box with nobody logged in.

That's the deadlock, and it's a clean one. The missing database row is a consequence of being headless, not an independent misconfiguration. On the machine that had it, the row was written one second before a console login that had already succeeded — macOS recording a grant it never needed.

The diagnostic that would have found this in one command:

ssh <host> 'system_profiler SPDisplaysDataType | grep -A3 Displays:'

No output means headless, which means login-window VNC will connect and then show you nothing.

What actually fixes it

And the reboots were panics all along

We spent weeks attributing the unexplained reboots to automatic OS updates. They were kernel panics:

panic(cpu 10): watchdog timeout: no checkins from watchdogd in 90 seconds

/Library/Logs/DiagnosticReports/*.panic names the cause. The file's timestamp is when it was written on the next boot, not when the panic happened, which is why it never lined up with the outage and we kept dismissing it. Check for a panic file before blaming the update mechanism.

The diagnostic order we use now

  1. uptime — fresh boot?
  2. stat -f "%Su" /dev/console — root means no GUI session, which means every user LaunchAgent is dead.
  3. launchctl list | grep -c <prefix> — 0 confirms it.
  4. ls -t /Library/Logs/DiagnosticReports/*.panic — was this a panic?
  5. If unreachable: probe from another host. Fast publickey rejection with password-only auth means FileVault pre-boot; SSH in with the password to unlock.

Key takeaways

  1. A Mac at the FileVault pre-boot screen answers SSH. Fast publickey rejection plus password-only auth, with every other port closed, is the fingerprint. It is not a dying machine.
  2. You can unlock FileVault over SSH on Apple Silicon running macOS 26 — but only over wired Ethernet, and only from a host on the same subnet, because the VPN and the Wi-Fi password both live on the encrypted volume.
  3. User LaunchAgents are not reboot-safe. An unattended unlock creates no gui/$UID domain, so they never load, and you cannot bootstrap the domain over SSH. Use system LaunchDaemons for anything that must come back.
  4. lsof as a non-root user cannot see root's launchd sockets and returns empty either way. Probe the port from another host instead.
  5. ARDAgent is a user agent; com.apple.screensharing is a system daemon. Only the second one can serve a login window, and kickstart cannot enable Screen Sharing at all on modern macOS.
  6. A headless Mac cannot grant itself login-window screen capture. With a display, WindowServer serves capture directly; without one, the path requires a permission only a GUI user or MDM can write. A dummy HDMI plug retires the entire failure class.
  7. A panic report is timestamped when it is written, not when the panic occurred. That mismatch is why we blamed software updates for a month.

Everything in these notes I also do for hire: local AI set up on hardware you own, configured on-site, then handed over with enough documentation that you do not need me afterward. If that sounds more useful than another weekend of reading forum threads, the details are at /work. No obligation from an email, and the posts stay free either way.

See pricing and book an audit →