#1199·whatsmeow

Can't connect additional iOS devices right now

Author: egorlbCreated Jul 8, 2026Updated Aug 5, 2026
Labelscan't reproduce

I started running into a consistent pairing failure with whatsmeow on a real iPhone built via gomobile.

The failure reproduces only when all of the following hold:

The client is built via gomobile bind into an xcframework and runs on a real iPhone (arm64). The TLS connection is opened the default whatsmeow way — through Go's crypto/tls (or through utls without further tuning). The WebSocket-upgrade HTTP headers are the defaults injected by coder/websocket + whatsmeow itself (no User-Agent, no Sec-Fetch-*, no Cookie). Reproduction Take a whatsmeow-based app on a real iPhone with an empty store. Generate a QR via GetQRChannel + Connect. Scan the QR with the official WhatsApp app on a phone. The official client returns "Can't link new devices right now". Wait an hour, retry — including a clean Delete App + fresh install, a different (untouched today) WhatsApp account, switching between Wi-Fi and cellular. Every time the same error. In parallel open web.whatsapp.com in Safari on the same Mac (same network, same IP) and scan the QR with the same account — pairing goes through on the first try, no errors. Key observation The following changes do not fix it:

Bumping whatsmeow to the latest commit or rolling back to a previous one (tested pre-passkey 37913c82b711 and current b572e5bcb92b). Swapping the PairClient value passed to PairPhone (Chrome / Safari / OtherWebClient — all three behave identically). Setting store.DeviceProps.RequireFullSync = true. Overriding PlatformType and Os in DeviceProps (tried CHROME/SAFARI, Mac OS X / iOS combos). Wiping identity_key via Delete App + a totally fresh install. Switching between Wi-Fi, cellular and different IPs. Using a clean WhatsApp account that had no pairing attempts today. Adding NSAppTransportSecurity exceptions in Info.plist. Note that simulator and real device reproduce identically as long as the whatsmeow HTTP client sends requests with the default headers.

The only thing that actually helped was bringing the WebSocket-upgrade request byte-identical to what a real browser sends to wss://web.whatsapp.com/ws/chat.

I captured the full upgrade request from a real Chrome / Safari session to WhatsApp Web via mitmproxy and diffed it against ours. The findings:

We were not sending Sec-WebSocket-Extensions: permessage-deflate (browsers always add this). We were missing Accept: /, Accept-Encoding: gzip, deflate, br, zstd, Priority: u=3, i. We were missing Cookie: wa_web_access_token=...; wa_ul=...; wa_web_lang_pref=en_US — the browser gets these from a Set-Cookie on GET https://web.whatsapp.com/ that happens before the WebSocket. Sec-Fetch-Dest was empty instead of websocket, and Sec-Fetch-Site was cross-site instead of same-origin. User-Agent was missing entirely, so Go was defaulting to Go-http-client/1.1. Once I did the following in the HTTP client wired via SetPreLoginHTTPClient / SetWebsocketHTTPClient:

Explicitly set every one of those headers to match Safari 26.5 / Mac OS X 10_15_7 byte-for-byte. Attached an http.CookieJar and issued GET https://web.whatsapp.com/ before the first Connect to seed the session cookies. Pairing succeeds on the first attempt, consistently. WhatsApp accepts the connection and sends HistorySync as normal.

Extra context There's a third-party app in the App Store (whatsmeow-based, also gomobile-bound iOS xcframework) whose binary contains refraction-networking/utls and what looks like their own whatsmeow fork — so someone else clearly went through the same fixes independently.

Timing-wise this seems to line up with WhatsApp tightening anti-abuse checks against non-official clients. whatsmeow's defaults (Os: whatsmeow, Version: 0.1.0, Go-http-client/1.1 on the HTTP side, no cookies, no Sec-* headers) apparently became enough of a signature for the server to return Can't link new devices right now before any protocol-level exchange happens at all.

Has anyone else hit this on real iOS devices?