The Dock Told My Mac to Unplug a Monitor That Was Still Plugged In

I have a CalDigit TS5 Plus. It is a nice dock, genuinely. It was one of the few I could find that does 10GbE and drives two monitors and delivers the full 140W to charge my laptop, all over a single cable. That single-plug convenience is the entire reason you buy a dock like this in the first place. It is also, right now, occasionally taking my whole OS session down with it. I want to be precise about that, because “dock problem” undersells it: this is not a monitor glitch you shrug off. It is the kind of failure where the machine freezes solid, you lose whatever you were in the middle of, and you reboot. If it catches you mid-demo or mid-screenshare, you also get to explain to a room full of people why your screen just turned into a still photograph of itself. Getting that taken seriously by CalDigit took more pushing than I think it should have, which is the other half of this post.

This post is two things. First, it is a genuinely interesting root-cause story about a spurious hotplug event, a DisplayPort retimer, and how the macOS display pipeline handles (or fails to handle) a lie told to it by hardware. Second, it is about what happens when you hand a support organization a fully diagnosed bug with logs and get the cable-reseat script back anyway, and about how far you have to push to reach someone who can act on it. That half ends better than it looked like it was going to. Read whichever one you came for.

The setup

Nothing exotic:

  • CalDigit TS5 Plus, Thunderbolt firmware 64.1, micro firmware 1.11.0
  • MacBook Pro (M5 Max), macOS 26.5.1 (build 25F80), Thunderbolt 5
  • Two Acer B326HUL monitors, 2560x1440 at 60 Hz, both driven off the dock’s downstream Thunderbolt/USB-C ports
  • Keyboard, mouse, 10GbE, audio out. The CalDigit cable that came in the box. That is it.

The monitors are old. Keep that in mind, it matters later.

What happened

One afternoon, with nothing plugged or unplugged and nobody touching anything, one of the two external displays dropped. Within about 50 seconds the whole machine froze. Not a kernel panic, the kernel was fine, input was still reaching it. The compositor (WindowServer) wedged on its last frame and the UI turned into a photograph of itself. Closing and reopening the lid, which is the reflexive thing you do, made it worse: the recovery path failed to re-establish the link, the internal panel went black too, and I got to hard-reboot a laptop for the first time in a long while.

The old instinct here is “flaky cable, reseat it.” I want to be clear up front that this was not that. No cable moved. No power blipped. So I did what you are supposed to do when computers misbehave, which is stop guessing and go read what the machine actually recorded.

Reading the logs

macOS keeps a unified log, and the display subsystem is chatty in it if you know what to grep for. Here is the beginning of the end, from the Thunderbolt family driver:

kernel: (IOThunderboltFamily) IOThunderboltSwitch(0@0)::processPlugEvent - Thunderbolt HPD packet ... port = 1 plug = 0
kernel: (IOAccessoryManager) IOPortTransportState::terminate(): [Port-USB-C@1: DisplayPort@0] Terminating IOPortTransportStateDisplayPort

Read that first line carefully, because it is the whole story. plug = 0 is an HPD (hotplug detect) event that says “the thing on port 1 has been unplugged.” Except nothing on port 1 had been unplugged. The monitor was sitting right there, powered, physically connected, as it had been for weeks. The Thunderbolt switch inside the dock told the host that a device went away when it had not.

The host, having been told a monitor was yanked, does what you would do if a monitor was yanked. It tears the pipeline down:

kernel: (DCPEXT1) AppleDCPDPTXController::handleUnplugEvent unplug, sync=1
kernel: (DCPEXT1) AppleDCPDPTXController::deviceRemovalOccurred HPD Lost period over
kernel: (DCPEXT1) AppleDCPDPTXHDCP2Controller::abortHDCPNegotiation status=0xe00002c0

That is the macOS Display CoProcessor (the DCP, an on-die coprocessor that owns the display link on Apple Silicon) unwinding the connection and aborting an HDCP negotiation that had nothing left to talk to. Somewhere in the roughly 700 lines of teardown that followed, WindowServer stopped being able to make forward progress, and that is the freeze.

Then the lid-close recovery attempt, which is where it goes from “annoying” to “reboot”:

kernel: (DCPEXT0) AppleDCPMCDP2900::startLinkGated <error>
kernel: (DCP) IOMFB: clearing M3 reset

AppleDCPMCDP2900 is the interesting name here. The MCDP2900 is a Kinetic/Synaptics DisplayPort retimer, the active chip that sits in the signal path and reconditions the DP link. startLinkGated <error> is that retimer failing to bring the link back up on the recovery pass. So not only did we eat a bogus unplug, the re-link failed and took the internal panel down with it.

For completeness, the things I ruled out, because “did you try another cable” is coming:

  • Not a cable event. Nothing moved, and it is CalDigit’s own bundled cable.
  • Not a kernel panic. NVRAM clean, no panic report. Soft hang.
  • Not the GPU. Zero AGX / IOAccelerator faults. This is strictly the DCP display path, not rendering.

The hypothesis

Here is what I think is actually going on. It is a hypothesis, not a certainty, because I cannot see inside the dock’s firmware.

The trigger is almost certainly the monitor. These are old panels, and old panels do rude things: brief loss-of-signal blips, aggressive power-save transitions, marginal DP compliance. That part is not novel. Fabien Sanglard has a nice writeup (https://fabiensanglard.net/tb4/index.html) of a years-long docked-link reliability problem on an older CalDigit dock that only went away when he replaced an aging monitor. Different dock, different symptom, and he never fully root-caused it, so I am not claiming it is my bug. I am claiming the display is a chronically under-appreciated variable in dock link reliability, and that is well documented.

But “your monitor is old” is not the interesting part, and it is definitely not an acceptable fix. Here is the interesting part. The MCDP2900 retimer exists precisely to sit between a marginal sink and the host and condition the signal. Its entire job description is to be the buffer between a twitchy monitor and the transport layer. And instead of absorbing a momentary downstream glitch, it appears to be propagating that transient upstream as a full HPD unplug. A hiccup on the monitor’s side becomes a “the device is gone” packet to the host, and macOS then handles that packet badly enough to wedge the compositor.

So there are two bugs stacked on top of each other:

  1. The dock’s retimer converting a transient sink glitch into a full unplug instead of debouncing it. This is the one CalDigit can fix.
  2. The macOS DCP recovery path failing hard on a spurious unplug, including failing to relight the internal panel. This is Apple’s, and it is a separate writeup.

The fix I want from the dock side is not exotic. Add debounce or hysteresis on downstream HPD and loss-of-signal transitions before propagating an unplug upstream. Do not tear down a working tunnel because a monitor sneezed. Retimers on other platforms do exactly this.

And separately, give me a way to soft-reset the retimer from the host without cutting power. If the link ever gets into this stale state, being able to re-initialize the chip programmatically would be a genuine workaround, both as prevention and as recovery, and it would not require me to crawl under the desk and pull power on a dock with a dozen things hanging off it.

A note on how I actually did this

I have been debugging weird hardware and software for a couple of decades, and I can read a log file. But I would be misrepresenting how this went if I told you I sat down and pulled that one plug = 0 packet out of a multi-thousand-line unified log slice by hand, matched a driver name to a specific Synaptics retimer part, and cleanly separated the trigger from the failure, all on my own in an afternoon. I have started plenty of investigations like that. I finish a lot fewer of them than I would like, because the grunt work is enormous and it is easy to talk yourself into “eh, probably just a flaky cable” and move on.

What actually happened is I handed the logs to Claude and worked the problem with it as a partner. The part that used to be an afternoon of squinting at log show output, grepping for strings I half-remembered, and slowly convincing myself which subsystem was lying, was more like twenty minutes of back-and-forth. It pulled the phantom-unplug line out faster than I would have, connected AppleDCPMCDP2900 to the retimer, and kept me honest about the trigger-versus-failure distinction when I wanted to blame one side for everything. I still had to know which questions were worth asking and whether the answers held together, which is the part that does not automate. But the drudgery that normally keeps me from doing this kind of thing at all mostly evaporated, and what came out the other end is more accurate than what I would have produced alone.

I mention this for two reasons. One, credit where it is due. Two, it is directly relevant to the support story below. The barrier to producing a real, mechanism-level bug report used to be high enough that most people, reasonably, never cleared it. That barrier is dropping fast. The reports are going to get better and more numerous, not fewer, and a support process built on the assumption that end users cannot possibly hand you a correct diagnosis is going to be wrong about that more and more often.

The support experience

This is the part that made me open a text editor.

I did not send CalDigit a “my screen went black plz help” ticket. I sent them the setup, the incident timeline, the annotated log slice above, the ruled-out list, and three specific questions: is there newer firmware, is spurious HPD loss under dual-display load a known issue, and is there a supported way to soft-reset the retimer from the host. I did their triage for them so they could route it to an engineer instead of a script.

What I got back was the script.

The first reply told me 64.1 is the latest firmware (fine, useful), then that the dock “is designed to handle much more demanding display configurations” and they have “reliably run four 4K 144 Hz displays,” so two monitors “shouldn’t be an issue.” This misses the point entirely. I did not say the dock lacks bandwidth. I said it emitted a phantom unplug. Those are not the same class of problem, and throughput headroom has nothing to do with HPD debounce.

Then a set of triage questions I had largely already answered in my first mail, and a closing line that “rebooting the computer to resolve the issue suggests the cause is related to the host operating system, rather than the dock.” Which, sure, the hang lives in the host. I said that. But the host was reacting to a packet the dock’s own transport layer emitted. Blaming the host for handling a lie badly does not get the dock off the hook for telling the lie.

I answered everything, refined the diagnosis, and asked again for an escalation. The second reply was cleaner but firmer: before support can escalate, their lab has to reproduce the behavior. They have tested a range of DisplayPort monitors from DP 1.1 to DP 2.1 and never seen it. They have had no prior reports. The dock relies heavily on the host, drivers live in the host OS, sleep is managed by the OS. Testing with a different compatible computer would help, since it “currently appears to be host-related.”

I want to take the reproduction wall seriously for a second, because it is the crux. This is a fault that appears to need three things at once: an older, marginal sink, multi-week uptime, and a transient arriving while the link is live. A lab bench, running current in-spec monitors for a short session, is close to the ideal environment to never see this. “We can’t reproduce it” and “we’ve had no reports” are not evidence the bug is not real. They are exactly what a rare, uptime-and-hardware-dependent failure looks like from inside a support queue. The absence of reports is partly a function of how hard it is to report something like this through a funnel that wants a reproduction first.

That is the frustrating part. This kind of failure had finally landed in front of someone who reads kernel logs for fun, and I had handed over the mechanism rather than a symptom. That is the rare ticket that can short-circuit months of unactionable “my screen randomly goes black” reports from people who have no way to tell you why. Answering it from the same script as a bad cable means the most useful report you are going to get on the issue never reaches the people who could act on it.

And the “test on a different computer” ask, while reasonable in the abstract, runs into reality: this M5 Max is a corporate-managed machine and my only Thunderbolt 5 host. I cannot casually swap in a second one, and I said so.

Then they escalated, and it got better

On the fourth round, it finally moved. Somebody who works directly with CalDigit’s engineers stepped into the thread over the top of the script, and the conversation turned into the one I had been trying to have from the start.

He acknowledged the trigger outright. They have seen the spurious hot plugs I was describing, it has affected some users, for most of them it shows up as monitors flickering rather than a whole frozen session, and they are actively investigating it with the intent of fixing it in firmware. That is the direct opposite of the earlier “no prior reports,” and I appreciated getting it straight.

He was equally straight about the boundary of what they own. They still had not reproduced the frozen-UI cascade, having run TS5 Plus units at month-plus uptimes across two, three and four monitor configurations without a hang. So they take the phantom unplug. The part where macOS turns that unplug into a wedged compositor stays on Apple’s side of the fence, and honestly that matches my own read of the two faults.

Then he sent me a beta Power Delivery firmware to try, ahead of any public release.

I flashed it on July 20th. A month later, the freeze has not come back.

I want to be careful about how much that is worth. The original fault needed something like a week or more of uptime before it would show itself, and it was never reproducible on command. A month without a freeze is encouraging and it is not proof. When the only available evidence is an absence accumulating slowly, it is easy to talk yourself into a fix that is really just a quiet spell. If it returns, I will say so here.

A couple of things are still open. There is no supported host-side way to soft-reset the dock or its retimer, so a physical power cycle is still the only lever I have. And the firmware I am running is on the Power Delivery microcontroller, which is a different chip from the Thunderbolt controller, so whether the retimer itself ends up with debounce and hysteresis on downstream HPD is a question for a future Thunderbolt firmware.

What I wanted out of this was for someone to look at the mechanism and treat it as a firmware question. That is what ended up happening. I wish it had taken one round instead of four, and I think a report that arrives already diagnosed deserves a faster path than restarting triage from step one. But they got there, they were honest about what they had and had not reproduced, and they put an actual build in my hands rather than a form letter. Short of reproducing it again myself, or a Thunderbolt firmware release that closes it out properly, I am not sure what more I would ask them for.

I still like the hardware. I like it quite a bit more now that the thing it was telling my computer appears to have been a bug someone is chasing, rather than a fact of life I was expected to work around.