2026-06-15: mirroring is reproducibly stuck on Connecting again with Tailscale accept-routes still off, so the 06-14 it-works conclusion was wrong. _asquic endpoint resolves but the QUIC/AWDL datapath never completes; awdl0 bounce, full reboot, and phone radio cycle all failed. Reframed as an intermittent macOS 27.0 beta AWDL bug; QuickTime USB remains the workaround.
150 lines
12 KiB
Markdown
150 lines
12 KiB
Markdown
---
|
||
title: "iPhone Mirroring Hangs on 'Connecting…' — AWDL Data Stall (27.0 Beta)"
|
||
domain: troubleshooting
|
||
category: macos
|
||
tags: [macos, iphone-mirroring, continuity, awdl, rapport, quic, tailscale, mullvad, beta, channel-validation, aimesh, quicktime, usb]
|
||
status: published
|
||
created: 2026-06-09
|
||
updated: 2026-06-15
|
||
---
|
||
|
||
# iPhone Mirroring Hangs on 'Connecting…' — AWDL Data Stall (27.0 Beta)
|
||
|
||
## Update 2026‑06‑15 — REGRESSED; reproducibly stuck on "Connecting", and Tailscale was **not** the cure
|
||
|
||
> **Correction to the 2026‑06‑14 "it WORKS" update below.** On 2026‑06‑15 iPhone Mirroring is **reproducibly stuck on "Connecting to iPhone 16 Pro"** on MajorAir again — with Tailscale `accept-routes` *still* `false`. So the accept‑routes change was **correlation, not the fix**: this is an **intermittent macOS 27.0 beta AWDL bug, independent of Tailscale**.
|
||
>
|
||
> **Tried this round — all failed to establish a session:** Tailscale `accept-routes=false` (already in place) · `sudo ifconfig awdl0 down/up` · **full Mac reboot** · cycling the iPhone's Wi‑Fi + Bluetooth.
|
||
>
|
||
> **Log signature:** `rapportd` resolves the phone's `_asquic._udp.local` endpoint and `_companion-link` registers (discovery *succeeds*), but the QUIC‑over‑AWDL **datapath never completes into a live session** — `wifip2pd` loops on `AWDLDiscoveryTimeout (hasAdvertises=false)`. Each reset advanced the handshake one stage further (no‑advertises → resolve‑started → endpoint‑resolved) yet none reached a streaming session. **`llw0` never went active (0 bytes)** — confirming no A/V ever flowed, regardless of what the 06‑14 note measured.
|
||
>
|
||
> **Stance:** beta OS bug, **no reliable user‑side fix**. Use the **QuickTime USB mirror** workaround (below) when you actually need the phone on screen. The 06‑14 "it works on `llw0`" measurements were real *for that one session* but are **not reproducible** across seeds/sessions — treat mirroring as intermittently broken on the 27.0 betas. This re‑confirms the original **Root cause (conclusion)** section further down (a beta bug, "nothing in local config wrong"), which the 06‑14 update had prematurely overridden.
|
||
|
||
## Update 2026‑06‑14 (evening) — it WORKS; the "AWDL starvation" finding was the wrong interface
|
||
|
||
> iPhone Mirroring is now **working** on MajorAir — stable session, clean video, no missing icons — on **ch44/80** with Tailscale `accept-routes=false`. An earlier pass the same day blamed an "AWDL bulk‑path starving at ~90 B/s"; that was **measuring the wrong interface** and is corrected here.
|
||
|
||
**The video transport is `llw0` (low‑latency WLAN), not `awdl0`.**
|
||
Measured during an active session: **`llw0` ≈ 800 KB/s** (≈6 Mbps of real video), `en0` ~60 KB/s, **`awdl0` ~1 KB/s**. `awdl0` only ever carries AWDL *discovery/control* (~90 B/s) — whether mirroring works or not. So "90 B/s on `awdl0` = starved bulk path" was a **red herring**: the A/V stream rides `llw0`, which the earlier pass never measured.
|
||
|
||
**What was actually broken was session *stability*.** The `XPC_ERROR_CONNECTION_INTERRUPTED` / `MediaContinuityKit.TaskTimeoutError` teardown loop kept the `llw0` stream from ever sustaining (→ glitchy / missing icons). When the session holds, `llw0` streams clean.
|
||
|
||
**What changed (not cleanly isolated):** three things differed between the broken and working states — (1) the network fully **settled on ch44** over ~15 h (the failing ch44 test was minutes after a chaotic AiMesh re‑sync + reconnect scramble), (2) Tailscale **`accept-routes` was turned off** (it had been polluting IPv4 routing + the Continuity control plane), and (3) both devices slept/woke. Which one mattered is not yet proven.
|
||
|
||
**Open test — isolates Tailscale's role:** repro on **MajorMac** with *unaltered* Tailscale (`accept-routes` still **ON**). If mirroring breaks there but works on MajorAir (accept‑routes OFF), that pins Tailscale's accepted routes as the trigger. See [[MajorAir#Known Issues]] for the `accept-routes=false` fix.
|
||
|
||
**Still valid from earlier today:** congestion ruled out (router `chanim_stats` ch36 = 90 % idle, 86 % txop); the AiMesh / router infra notes below; and iPhone Mirroring is **wireless‑only — no USB transport** (for a wired screen view, use QuickTime, below).
|
||
|
||
> ⚠️ The iPhone‑radio `isValidChannel`/`awdl0` evidence cited in the original 2026‑06‑09 write‑up below describes AWDL *discovery* health, **not** the video path — read it in light of this correction.
|
||
|
||
**Wired workaround (works today, no AWDL):**
|
||
iPhone Mirroring is **wireless‑only — there is no USB transport** (confirmed: cable connected throughout, every attempt still used `awdl0`). For a wired view of the screen:
|
||
> **QuickTime Player → File → New Movie Recording → ⌄ next to record → select the iPhone** = full‑rate USB‑C screen mirror (view + record). Does **not** give remote control (tap/type) — that's unique to iPhone Mirroring.
|
||
|
||
**Infra notes (RT‑AX82U, AiMesh controller):**
|
||
- Router SSH is on **port 1025** (not 22); creds in Ansible vault (`router_username` / `router_password`).
|
||
- The 5 GHz channel is **AiMesh‑coordinated** and **resists CLI changes** — `wl chanspec` / nvram `wl1_chanspec` get re‑asserted by `acsd2` + AiMesh within seconds, even after `restart_wireless`. Only setting Control Channel to an **explicit value in the Web UI** holds mesh‑wide. Left "Auto" → acsd2 picks **36** (the cleanest channel).
|
||
- Any channel change triggers a **mesh re‑sync (~1 min) that drops all Wi‑Fi**; during it MajorAir falls back to the iPhone's **USB Personal Hotspot** (`en7` / `172.20.10.x`) and won't auto‑rejoin home Wi‑Fi while the hotspot feeds it internet (manual Wi‑Fi‑menu join needed).
|
||
- **Current state: 5 GHz on ch44/80** (same clean UNII‑1 spectrum as 36; left here to avoid another re‑sync — the Deck streams identically on 44).
|
||
|
||
**If it breaks again — troubleshooting checklist:**
|
||
1. **It's session stability, not bandwidth.** Look for teardown loops: `log show --last 3m --predicate 'process == "iPhone Mirroring"' | grep -iE "interrupt|timeout|endpoint"`.
|
||
2. **Measure the right interface** — video rides **`llw0`** (hundreds of KB/s when the screen is active), *not* `awdl0` (~90 B/s control is normal): `netstat -ib | awk '/<Link#/{print $1, $7}'` before/after a few seconds.
|
||
3. **Tailscale:** confirm `accept-routes=false` on the Mac (`tailscale debug prefs | grep RouteAll`) — see [[MajorAir#Known Issues]].
|
||
4. **Let the network settle** after any Wi‑Fi/channel change — an AiMesh re‑sync churns AWDL/Continuity state for a minute+; retry once stable.
|
||
5. iPhone: on home Wi‑Fi, near the Mac, **Personal Hotspot off**, not in Low Power Mode.
|
||
6. **Wired fallback that always works:** QuickTime → New Movie Recording → select the iPhone (USB‑C; view/record only, no control).
|
||
|
||
---
|
||
|
||
## Symptom
|
||
iPhone Mirroring on the Mac sits on **"Connecting…"** forever and never shows the iPhone screen.
|
||
- Mac: **macOS 27.0 dev beta** (build 26A5353q), MajorAir
|
||
- iPhone: **Major16Pro / iPhone17,1, iOS 27.0 dev beta**, same Apple ID (maj.linux@gmail.com)
|
||
|
||
## Root cause (conclusion)
|
||
A **bug in the iPhone Mirroring beta** (both devices on the `.0` developer seeds). The connection
|
||
authenticates, the AWDL peer-to-peer link comes up, the TLS handshake starts — then **bidirectional
|
||
data stalls ~2 seconds in** and the link is torn down. Deterministic, reproduces every attempt.
|
||
**Nothing in the local configuration was wrong.** Filed via Feedback Assistant; expected to clear in
|
||
a future seed.
|
||
|
||
Two *real but secondary* network-layer issues were found and fixed along the way (see below) — they
|
||
can block mirroring independently, but were not the cause of the final 2-second stall.
|
||
|
||
## The smoking gun (unified log)
|
||
Per connection attempt the sequence is always:
|
||
```
|
||
rapportd: Session start … linkType "AWDL", error "NoError" # link negotiated OK
|
||
iPhone Mirroring: Installing verify block for 1 authorized peer key(s)
|
||
boringssl: TLS client read_server_hello # iPhone DID respond
|
||
quic: path over awdl0 received event established / promoted to primary
|
||
nw_flow_connected: Transport protocol connected (socket)
|
||
… flow:connect_stalled @2.003s # stalls exactly ~2s in
|
||
quic_conn_log_summary: Connection attempts: 6, RETRY received: no, PTOs: 5 # packets sent, zero ACKs
|
||
[C1.1.1 … awdl0 … failed socket-flow (unsatisfied (No network route))] # link dropped (symptom, not cause)
|
||
```
|
||
The connection is pinned to AWDL (`allowed subtypes: wifi_awdl, prohibit fallback`), so once the
|
||
AWDL data path stalls there is no fallback and it fails. "No network route" is the *result* of the
|
||
teardown, not the trigger. The trigger is that after the initial handshake packets, **sustained
|
||
QUIC traffic over AWDL gets no ACKs** (PTOs).
|
||
|
||
## Investigation path (what was ruled out)
|
||
- **Discovery / proximity** — healthy throughout. BLE + Bonjour resolve the iPhone; `rapportd`
|
||
sees it with good RSSI, same iCloud (`DF < MyiCloud >`), `WiFiP2P`.
|
||
- **Tailscale (full-tunnel)** — with Tailscale connected, the attempt died at "No network route"
|
||
*before* even reaching AWDL. Cause: `RouteAll: true` (accept-routes / a `::/0` advertised route)
|
||
installs **IPv6 default routes via `utun`** (`default → fe80::%utun0..3`) that black-hole the
|
||
IPv6 path AWDL needs. **`tailscale down` is NOT enough** — it only sets `WantRunning=false`; the
|
||
macOS VPN *configuration* (`scutil --nc list` showed it still `Connected`) and the system
|
||
extension keep reasserting the routes across reboots. Must disable in **System Settings → VPN**.
|
||
- **Mullvad** — `mullvad-daemon` running; **"Local network sharing" was set to `block`**, which
|
||
blocks LAN/AWDL/multicast. Changed to **`allow`** (`mullvad lan set allow`). Kill-switch was off.
|
||
- **macOS firewall** — off. No Little Snitch/LuLu app installed.
|
||
- **Lockdown Mode** — off (iPhone).
|
||
- **OS-version mismatch** — ruled out; both Mac and iPhone on 27.0 dev beta.
|
||
- **Device trust / re-pairing** — there is **no local pairing record on the Mac** to reset.
|
||
`rapportd` lists the iPhone as **"PairedSys Conjectured"** = trust is *derived from the shared
|
||
Apple ID*, not a manual pairing. Forgetting the Mac on the iPhone does not force re-setup; the
|
||
Mac just re-derives the association from iCloud and reconnects. (App containers
|
||
`~/Library/Containers/com.apple.ScreenContinuity` and the rapport stores held no device record;
|
||
the "1 authorized peer key" lives in the protected system keychain.)
|
||
- **Reboots / airplane-mode toggle / Mac-side AWDL + rapportd reset** — no change.
|
||
|
||
## Secondary issues found & fixed (do these regardless)
|
||
1. **Mullvad** — set **Local network sharing = allow** (done). Required for any LAN/AWDL feature.
|
||
2. **Tailscale** — do not run **full-tunnel / accept a `::/0` route** while mirroring; it installs
|
||
IPv6 default routes via `utun` that kill the local link. Toggle the VPN off in System Settings
|
||
(not just `tailscale down`) if it ever needs to be fully out of the path.
|
||
3. **Orphaned Little Snitch network extension** — the app was uninstalled but its
|
||
`at.obdev.littlesnitch.networkextension` is still `[activated enabled]`
|
||
(`systemextensionsctl list`). Remove via **System Settings → General → Login Items &
|
||
Extensions → Network Extensions**. A zombie filter extension with no app behind it can
|
||
black-hole traffic.
|
||
|
||
## Status / next steps
|
||
- **No user-side fix.** Filed in Feedback Assistant.
|
||
- Debug capture saved: `~/Desktop/iPhoneMirroring-debug-20260609-0026.txt` (summary + log narrative).
|
||
For a full report, trigger a sysdiagnose (**⌃⌥⇧⌘ + .**) right after reproducing and attach it.
|
||
- VPNs restored after session: Tailscale back up; Mullvad left disconnected with LAN sharing = allow.
|
||
|
||
## Useful diagnostic commands (for next time)
|
||
```bash
|
||
# Connection narrative
|
||
log show --last 10m --style compact --predicate \
|
||
'(subsystem == "com.apple.MediaContinuityKit") OR (process == "iPhone Mirroring")' | tail -60
|
||
# rapport / AWDL negotiation
|
||
log show --last 5m --style compact --predicate 'process == "rapportd"' | grep -iE "AWDL|Pair|Session"
|
||
# VPN config really on? (CLI "down" lies)
|
||
scutil --nc list ; scutil --nc status "Tailscale"
|
||
# IPv6 default routes hijacked by utun?
|
||
netstat -rn -f inet6 | awk '$1=="default"{print}'
|
||
# Active system extensions (filters/VPNs)
|
||
systemextensionsctl list
|
||
# Mullvad LAN sharing
|
||
mullvad lan get
|
||
```
|
||
|
||
## Related
|
||
- `macos-mirrored-notification-alert-loop.md` (other Continuity issue)
|
||
- Hosts/VPN context: MajorTwin project doc (Tailscale tailnet, 100.x addresses)
|