Highlights
✨ pymobiledevice3 doctor — what this machine can reach a device with, and why not
"No device found" has too many host-side causes to guess at: usbmuxd not running, a daemon that cannot discover devices over Wi-Fi, mDNS blocked by macOS's Local Network privacy setting, no userspace stack installed. Each was diagnosable only by knowing which internal to call.
doctor runs those checks and says what to do about each. It needs no device attached — which is the state being explained — and when one is attached it also reports what that device is and whether it is ready for developer work.
pymobiledevice3 doctorpymobiledevice3 11.18.0 on macOS-27.0-arm64, python 3.14.6
Host — this works:
Device plugged in iPhone (00008030-...), and usbmux lists it
usbmux daemon reachable, 2 device(s): ... over Network, ... over USB
Wi-Fi devices a device is listed over the network
Service access opened a lockdown connection to 00008030-...
Tunnel (native) remotepairingd answered, 1 device(s)
Tunnel (userspace) pmd-pytcp 0.3.7
Host — this does not:
Bonjour discovery every query was refused on all 27 interface(s) ([Errno 65] No route to host)
so: `bonjour` commands and --mobdev2 find nothing, and `remote` Wi-Fi flows cannot start
fix: on macOS allow the app running this command (e.g. your terminal) under System Settings >
Privacy & Security > Local Network, then restart it; elsewhere check the firewall
Device — this works:
Device iPhone15,4 running 18.4, paired
Developer mode enabled
Developer image a Personalized image is mounted
It exits non-zero when something is broken and --json gives the same report as data, so it is worth pasting into a bug report — the issue template now asks for it instead of asking you to list host OS, Python, versions and device details by hand.
Checks prefer proving a capability to inferring one. A device listed over the network proves Wi-Fi discovery whatever the daemon is; opening one lockdown connection proves the daemon implements Connect — which usbmuxd2 shipped without until 0.64, where listing worked while every service connection failed (#1147). Where neither proof nor attribution is possible the check says so rather than showing a green tick it cannot justify. Daemon identity is consulted only to explain what could not be exercised: the service registry on Windows separates the Microsoft Store "Apple Devices" app from the iTunes package, and on Linux only usbmuxd2 links Avahi.
🐛 The Wi-Fi tunnel for iOS 17.0-17.3 targets the device you asked for
On a device with no CoreDeviceProxy, --userspace falls back to RemotePairing over bonjour. That lookup ran unnamed whenever a single device was attached and no --udid was given, so every paired device on the network was contacted and the first answer won — after identifying a device over USB, the tunnel could be built against a different phone, and the services that lost the race were opened and never closed. It now asks for the device the lockdown handshake already named.
📚 Telling "the device is silent" apart from "this host has not listed it"
Reversing both sides of the Wi-Fi path settled a long-standing confusion. EnableWifiConnections applies the moment it is written — measured against a device, the advert disappears and returns within seconds — so re-running lockdown wifi-connections on cannot be the fix when the device is already advertising, and the daemon that has not listed it is the host's. The troubleshooting guide now gives the two commands that tell the cases apart, the cheap host-side nudges, and a way to make the device re-announce itself without changing any setting:
pymobiledevice3 notification post com.apple.mobile.lockdown.BonjourServiceChangedWi-Fi reach is also per host: a device advertises one authentication tag for each host it is paired with, so every machine that wants to reach it over Wi-Fi must be paired with it over USB first. The installation guide now points Windows users at the classic iTunes package rather than the Store app, whose service does not discover devices over Wi-Fi at all.
What's Changed
- 1ab6f10 doctor: Stop the browse-window assertion tripping on a coarse clock (#1983) (@doronz88)
- 957e6c3 doctor: Skip the sysfs tree test where its node names are illegal (#1983) (@doronz88)
- 98acd37 docs: Separate a device that is advertising from a host that has listed it (#1984) (@doronz88)
- 0b92595 doctor: Check daemon identification against a real usbmuxd in CI (#1983) (@doronz88)
- 66010be doctor: Pin the last restated rule and the advice it gives (#1983) (@doronz88)
- f2b7ecb doctor: Derive the rules it reports on instead of restating them (#1983) (@doronz88)
- e698751 doctor: Describe the attached device, and ask for the report in issues (#1983) (@doronz88)
- 66857ef doctor: Drop the ioregistry cast now that it ships types (#1983) (@doronz88)
- 69a37bd doctor: Report on hosts broken in ways the checks did not expect (#1983) (@doronz88)
- 6f1a5d3 doctor: Ask the Linux kernel what is plugged in too (#1983) (@doronz88)
- c21fefb doctor: Colour the report so the answer is visible at a glance (#1983) (@doronz88)
- bebf4d3 doctor: Ask the host what is plugged in (#1983) (@doronz88)
- b7ae0a9 doctor: Report by capability instead of by internal name (#1983) (@doronz88)
- 4457b88 doctor: Add a host check for what this machine can reach a device with (#1983) (@doronz88)
Full Changelog: v11.17.2...v11.18.0