Skip to content

[bug]: Disconnect can strand a display permanently (stale WindowServer display config) #40

Description

@Jordanburch101

OpenDisplay version

0.8.2 (build 14)

Build flavor

Full

macOS version / build

26.6.1 (25G76)

Mac model & chip

MacBook Air M5 (Mac17,4), Apple Silicon

Display model(s)

Samsung Odyssey G85SB 34" OLED ultrawide (3440x1440 @ 120Hz) + built-in Retina

Connection route

Thunderbolt dock; also reproduced on Direct USB-C/DP

Steps to reproduce

  1. External display connected and running normally at 3440x1440 @ 120Hz.
  2. Use OpenDisplay to disconnect the external display.
  3. Attempt to bring it back (Reconnect All, the reconnect hotkey, or replugging).

Expected vs actual behavior

Expected: the display reconnects.

Actual: the display never comes back, and is unrecoverable by any normal means.
macOS enumerates no external display at all — no entry in System Information, no
CGDisplay. The cable stays electrically live: the monitor reports signal present and
refuses to sleep, while showing black.

It survived every recovery I tried: Reconnect All, monitor power button, monitor
disconnected from mains for 30+ seconds, dock power-cycled from mains, different
cables, and connecting the monitor directly to the Mac with the dock removed
entirely. It also survived reboots.

Root cause

macOS keeps saved per-display configuration in the WindowServer display plists, keyed
by display identity, and restores it on connect instead of renegotiating. Once that
saved entry can no longer be applied, the restore fails and macOS gives up rather than
falling back — so you get no display at all rather than a degraded mode.

Here, /Library/Preferences/com.apple.windowserver.displays.plist held an entry for
the monitor's UUID at 3440x1440 / 120Hz / 10-bit that could no longer be applied. The
file was 500 lines. After clearing it and rebooting it rebuilt at 42 lines, and the
monitor came back at 120Hz within four seconds of plugging in, first attempt.

Since disconnect is a macOS-side display-configuration operation, it writes to
exactly these files. That's consistent with this being reproducible only via that
feature and not via physically unplugging.

A workaround that misleads

Turning off the monitor's "Input Signal Plus" makes the display appear again. This
looks like a fix but isn't: it changes the advertised EDID, so the monitor presents a
different identity and simply sidesteps the bad entry. The cost is losing the
high-refresh mode — it drops to 60Hz and, per other users with this panel, cuts out
intermittently.

Recovery that actually worked

With displays unplugged:

rm -f ~/Library/Preferences/ByHost/com.apple.windowserver.displays*.plist
sudo rm -f /Library/Preferences/com.apple.windowserver*.plist

then reboot immediately.

Two things worth flagging for anyone hitting this:

  • The reboot must follow with no gap. WindowServer holds this config in memory and
    flushes it straight back if you delete while logged in. I verified this — the
    recreated file was byte-identical to the one I'd just deleted, same 500 lines.
  • sudo killall Dock appears in widely-copied versions of this recipe. It seems to
    trigger that write-back and defeat the fix. Omitting it worked.

Was a recovery action needed?

Manual intervention

Suggestions

  1. Make disconnect reversible. Record the pre-disconnect configuration and restore
    it, and verify the display actually returned before reporting success.
  2. Surface the failure. When Reconnect All can't restore a display, say so and
    point at this recovery path. In my case managed-offline.json and the checkpoint's
    managedOffline were both empty while the display was still stranded — the app had
    lost track of the disconnect entirely. The checkpoint generation had reset to 1 with
    txn_baseline, so there was nothing left to roll back to.
  3. Warn where DDC is unavailable. IOAVService was 0 throughout here, since the
    display sits behind a Thunderbolt dock. With no DDC channel, the software disconnect
    is the only way in or out, so stranding is unrecoverable from inside the app.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions