my apple watch paired fine. then it forgot.
update, october 3: this turned out to be fixed by updating the iphone to ios 27.2, not by anything on the watch. the follow-up has the evidence and its limits: the watch wasn't broken. my iphone was behind. what follows is the investigation as i wrote it at the time.
a writeup of an apple watch ultra 2 that disappeared from xcode 27 and never came back. this one isn't solved. what i have is a much narrower description of the failure than "it won't connect", built from logs on the mac and on the watch, one experiment that changed my theory, and a dead end with an independent pairing host. it's reported to apple as FB24924229.
tl;dr
- the watch went
unavailableindevicectl, then vanished. re-pairing it from device hub succeeds every time: srp pair-setup m1–m6,setupManualPairing succeeded. - ~30 ms later the channel closes. an iphone does exactly the same, then reconnects 1.4 s later with
verifyManualPairing. the watch never does. - watches pair into the mac, and the mac only listens while the pairing sheet is open. when i kept it listening, the watch came back 23 s later, but asked for a brand-new pairing instead of verifying the one it had just finished. it loops.
- so the mac side works. the watch doesn't keep, or doesn't use, its half of the pairing. that code lives in watchos, which i can't change.
the symptom
$ xcrun devicectl list devices
iPhone (2) available (paired) iPhone 15 Pro Max
iPad available (paired) iPad Pro 12.9-inch
(no watch)
$ xcrun devicectl manage pair --device <watch>
The specified device was not found (1000)
device hub showed "currently unavailable — must be nearby". installing from the iphone's watch app filled halfway and stalled. bluetooth, wi-fi, the cable, developer mode and the app itself were all ruled out first. the full list is in what-i-tried.md.
1. the pairing succeeds, then the channel closes
remotepairingd on the mac shows a complete, successful pairing, followed by a close:
22:21:41.314 PairSetup server done -- client authenticated
22:21:41.314 tcp-79: Pairing session of kind setupManualPairing succeeded
22:21:41.344 tcp-79: received error reading message <-- +30 ms
22:21:41.345 tcp-79: authenticated -> invalidated
22:21:41.663 DeviceHub: Beaconing pairing session explicitly ended by client
my first write-up called that 30 ms close the bug. it wasn't.
2. a control: the iphone does the same thing
after resetting location & privacy, the iphone also had to be re-paired, one minute before the watch, on the same mac:
| setup succeeded | channel closed | reconnect | |
|---|---|---|---|
| iphone | 10:54:22.173 | +36 ms | +1.4 s verifyManualPairing → available |
| watch | 10:55:08.951 | +29 ms | none |
the close is normal: setup ends, then the device reconnects on a verified session. what's missing is the watch's reconnect. i rewrote the repo and the apple report to say that. an earlier draft had used an ipad reconnect as the control, which wasn't a fresh setup, so it wasn't a fair comparison either.
3. watches pair into the mac
the mac finds an iphone by browsing _remotepairing._tcp. the watch never advertised that, and i'd listed "the watch doesn't announce itself" as the main hypothesis. reading a live log stream during pairing showed the flow is the other way round for a watch:
22:21:16 remotepairingd: Started listening for network pairing <-- sheet opened
22:21:41 setupManualPairing succeeded
22:21:41 DeviceHub: Beaconing pairing session explicitly ended <-- sheet closes itself
the watch connects to the mac's _remotepairing-pairable-host._tcp listener, and that listener exists only while device hub's pair nearby device… sheet is open. the sheet closes itself ~300–400 ms after setup. so the watch not advertising is probably by design, and a new theory showed up: maybe the watch tries to reconnect, but there's nothing left to connect to.
4. keeping the mac listening
reopening the sheet by hand left a 15 s gap and nothing came back. to close the gap i wrote watch-pair-keeper.sh: it tails remotepairingd, and the moment device hub drops the listener it clicks pair nearby device… again through system events, then reports whether the watch verified. before using it live i replayed a captured log through it to check the timings.
22:53:00.613 setupManualPairing succeeded (watch) closed at +305 ms
22:53:01.533 mac listening again gap: 615 ms
22:53:23.502 Network pairing peers updated. Total count: 1 <-- the watch is back
22:53:23.558 PairingData(startNewSession: true, kind: setupManualPairing)
22:53:23.559 DeviceHub: Presenting pairing challenge <-- a new code
22:53:33.642 setupManualPairing succeeded (watch) closed again at +286 ms
that disproved my theory and gave a better answer. the watch does reconnect. but it asks for a new pair-setup, not pair-verify, as if it had forgotten the pairing it finished 23 seconds earlier. the mac stored the pairing and was listening; the watch didn't use its side. setup, close, setup, close.
one false positive worth noting: the script flagged a verifyManualPairing at 22:53:59. it was the ipad. the script only counts a verify when the next authenticated line carries the watch's udid prefix, which is why it didn't report success.
5. the watch's own logs
a watch sysdiagnose (crown + side button, then out through the iphone) showed remotepairingdeviced starting the pairing normally: it resolves the mac's advert, connects over wi-fi, lockdownShouldDisableDevicePairing: NO, "manual pairing in progress". then the archive has no lines from any process between 22:21:34.1 and 22:21:48, so the watch side of the close at 22:21:41 isn't there. (the first archive that came through was the iphone's. the filename says iPhone-OS vs Watch-OS; check it before digging in.)
6. an independent host
if the mac side can't be changed, maybe it can be replaced. pymobiledevice3 11.19.1 has remote pair-host, which advertises a pairable host and accepts a device-initiated pairing. the watch listed it under developer ▸ paired macs ▸ other devices, asked for the watch passcode, and then never opened a tcp connection.
apple's host is reached over ipv6 link-local; pymobiledevice3 binds 0.0.0.0 and only publishes an a record. so i wrapped it (pair_host_dualstack.py) to listen on both families and advertise the aaaa record too. same result: no connection, over either. device hub also runs a "beaconing" session that pymobiledevice3 doesn't. whether the watch needs it, i don't know yet.
where it stands
- the fault is on the watch: after a successful setup it comes back asking for a new one.
- removing the mac on the watch (developer ▸ unpair this device) and pairing again doesn't change it.
- nothing on the mac side fixed it. the only step left untried is erasing the watch.
- the pairing-loop evidence went to apple as a follow-up to FB24924229.
mistakes i made along the way
- calling the 30 ms close the bug. an iphone control showed it's normal. corrected in the repo and the report.
- "the watch doesn't announce itself" as the main hypothesis. watches pair into the mac. crossed out.
- "the mac stops listening too early" as the fix. the experiment i built to prove it disproved it, and that's how the real finding showed up.
takeaways
- a control device is worth more than another theory. the iphone doing the same close in the same minute reframed the whole problem.
- read who connects to whom. i assumed the mac finds the watch. it's the reverse, and that changed which absence mattered.
- an experiment that fails to fix something can still be the result. keeping the listener open didn't fix pairing; it exposed the loop.
- "can't be fixed from here" should come with evidence. apple's host, my listener fix and an independent host, all with timestamps.
links
- kisnner26/watchos27-pairing-bug: sanitized logs, timeline, tools and the feedback assistant text
- doronz88/pymobiledevice3, the independent host used in step 6