Skip to content

fix: recover headless WalletConnect connects after a disconnect - #5728

Merged
0xmkh merged 4 commits into
mainfrom
fix/headless-wc-reconnect-after-disconnect
Jul 31, 2026
Merged

fix: recover headless WalletConnect connects after a disconnect#5728
0xmkh merged 4 commits into
mainfrom
fix/headless-wc-reconnect-after-disconnect

Conversation

@enesozturk

Copy link
Copy Markdown
Contributor

Problem

On a headless host (AppKit with no scaffold-ui — e.g. WalletConnect Pay's Buyer Experience), connecting a wallet after disconnecting a previous one leaves the page unusable until a reload: taps open nothing, and every subsequent attempt surfaces a connection error. Reported as WCPBX-1014.

Two gaps, both unreachable in headful mode (w3m-connecting-wc-mobile only fires onConnect once it has a URI, and its "try again" clears the error):

  1. wcError is sticky. It is set in connectWalletConnect's catch, but cleared only by a successful mobile deeplink (onConnectMobile) or the headful "try again" button. A headless host that hit one error reads as failed for the rest of the page's life.
  2. A mobile deeplink with no URI fails silently. ConnectionControllerUtil.onConnectMobile is a no-op when wcUri is missing — which is exactly the state a disconnect leaves behind (resetWcConnection clears the URI). connectWallet() resolved as though a wallet had been opened, so the host sat in a connecting state waiting on a redirect that never fires.

Change

  • ConnectionController.connectWalletConnect clears wcError when a new attempt starts (same thing headful's "try again" does manually).
  • New ConnectionControllerUtil.assertWcUriForDeeplink(), called by both headless connect paths (HeadlessWalletUtil.connect and useAppKitWallets' connect) before the mobile deeplink branch. The attempt now rejects — which also clears connectingWallet via the existing catch — so the host can pre-fetch a URI and retry. It cannot be fetched here: the redirect has to happen inside the tap (iOS Safari blocks one issued after an await).

Deliberately not changed: resetWcConnection does not clear wcError, because w3m-connecting-wc-view sets the error and then calls resetWcConnection — clearing it there would wipe the headful error UI.

Tests

  • connectWalletConnect clears a previous wcError on a new attempt.
  • HeadlessWalletUtil.connect rejects and clears connectingWallet when the URI is gone, without calling onConnectMobile.
  • Existing mobile-deeplink test now sets a URI (it relied on the silent no-op).

packages/controllers + packages/scaffold-ui suites pass; the 5 failing files on this branch (w3m-connecting-wc-mobile universal-link href, ChainController, and three localStorage/OTP view files) fail identically on main.

🤖 Generated with Claude Code

A headless host (no scaffold-ui) could not connect any wallet after
disconnecting one: taps opened nothing and every attempt reported a
connection error until the page was reloaded.

- `wcError` was only cleared by a successful mobile deeplink or the
  headful "try again" button, so one failure left every later attempt
  reading as failed. A new `connectWalletConnect` attempt now clears it.
- `onConnectMobile` no-ops without a pairing URI, and a disconnect clears
  the URI (`resetWcConnection`). The headless connect paths now reject via
  `assertWcUriForDeeplink` instead of resolving as if a wallet had opened,
  so the host can pre-fetch a URI and retry rather than wait on a redirect
  that never fires.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 543cfcd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 26 packages
Name Type
@reown/appkit-utils Patch
@reown/appkit Patch
@reown/appkit-cdn Patch
@reown/appkit-cli Patch
@reown/appkit-codemod Patch
@reown/appkit-common Patch
@reown/appkit-core Patch
@reown/appkit-experimental Patch
@reown/appkit-pay Patch
@reown/appkit-polyfills Patch
@reown/appkit-scaffold-ui Patch
@reown/appkit-siwe Patch
@reown/appkit-siwx Patch
@reown/appkit-testing Patch
@reown/appkit-ui Patch
@reown/appkit-universal-connector Patch
@reown/appkit-wallet-button Patch
@reown/appkit-wallet Patch
@reown/appkit-controllers Patch
@reown/appkit-adapter-bitcoin Patch
@reown/appkit-adapter-ethers Patch
@reown/appkit-adapter-ethers5 Patch
@reown/appkit-adapter-solana Patch
@reown/appkit-adapter-ton Patch
@reown/appkit-adapter-tron Patch
@reown/appkit-adapter-wagmi Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
appkit-demo Ready Ready Preview, Comment Jul 29, 2026 4:17pm
appkit-gallery Ready Ready Preview, Comment Jul 29, 2026 4:17pm
appkit-headless-sample-app Ready Ready Preview, Comment Jul 29, 2026 4:17pm
appkit-laboratory Ready Ready Preview, Comment Jul 29, 2026 4:17pm
9 Skipped Deployments
Project Deployment Actions Updated (UTC)
appkit-basic-example Ignored Ignored Jul 29, 2026 4:17pm
appkit-basic-sign-client-example Ignored Ignored Jul 29, 2026 4:17pm
appkit-basic-up-example Ignored Ignored Jul 29, 2026 4:17pm
appkit-ethers5-bera Ignored Ignored Jul 29, 2026 4:17pm
appkit-nansen-demo Ignored Ignored Jul 29, 2026 4:17pm
appkit-wagmi-cdn-example Ignored Ignored Jul 29, 2026 4:17pm
ethereum-provider-wagmi-example Ignored Ignored Jul 29, 2026 4:17pm
next-wagmi-solana-bitcoin-example Ignored Ignored Jul 29, 2026 4:17pm
vue-wagmi-example Ignored Ignored Jul 29, 2026 4:17pm

Request Review

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Visual Regression Test Results ✅ Passed

⚠️ 20 visual change(s) detected

Chromatic Build: https://www.chromatic.com/build?appId=6493191bf4b10fed8ca7353f&number=995
Storybook Preview: https://6493191bf4b10fed8ca7353f-hmegnzeyoq.chromatic.com/

👉 Please review the visual changes in Chromatic and accept or reject them.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Check

All bundles are within size limits

📊 View detailed bundle sizes

> @reown/appkit-monorepo@1.7.1 size /home/runner/work/appkit/appkit


> size-limit

@reown/appkit - Main Entry
Size limit:   80 kB
Size:         75.97 kB with all dependencies, minified and gzipped
Loading time: 1.5 s    on slow 3G
Running time: 540 ms   on Snapdragon 410
Total time:   2.1 s
@reown/appkit/react
Size limit:   236 kB
Size:         235.21 kB with all dependencies, minified and gzipped
Loading time: 4.6 s     on slow 3G
Running time: 1.1 s     on Snapdragon 410
Total time:   5.7 s
@reown/appkit/vue
Size limit:   80 kB
Size:         75.97 kB with all dependencies, minified and gzipped
Loading time: 1.5 s    on slow 3G
Running time: 525 ms   on Snapdragon 410
Total time:   2.1 s
@reown/appkit-scaffold-ui
Size limit:   220 kB
Size:         214.5 kB with all dependencies, minified and gzipped
Loading time: 4.2 s    on slow 3G
Running time: 381 ms   on Snapdragon 410
Total time:   4.6 s
@reown/appkit-ui
Size limit:   500 kB
Size:         13.16 kB with all dependencies, minified and gzipped
Loading time: 258 ms   on slow 3G
Running time: 181 ms   on Snapdragon 410
Total time:   438 ms

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 78.6% 40221 / 51168
🔵 Statements 78.6% 40221 / 51168
🔵 Functions 76.09% 4295 / 5644
🔵 Branches 86.71% 9840 / 11348
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/controllers/exports/react.ts 88.45% 87.64% 46.15% 88.45% 73-82, 108-116, 133, 148, 156, 174, 438, 452, 475, 482, 506, 520, 539-540, 542-551, 568-571
packages/controllers/src/controllers/ConnectionController.ts 84.93% 88.28% 53.76% 84.93% 176, 237-241, 244-248, 296-300, 308-309, 316-335, 350, 354, 358, 362, 366, 370, 374, 378, 382, 386, 469, 473, 495-499, 514-516, 527-528, 563-564, 574-575, 577-578, 586-587, 589-590, 609-610
packages/controllers/src/utils/ConnectionControllerUtil.ts 79.48% 82.35% 84.61% 79.48% 28-29, 36-37, 82, 90-96, 144-155, 152
packages/controllers/src/utils/HeadlessWalletUtil.ts 92.24% 97.05% 88.23% 92.24% 132-139, 200, 214
Generated in workflow #17214 for commit 543cfcd by the Vitest Coverage Report Action

Comment thread packages/controllers/src/controllers/ConnectionController.ts Outdated
Comment thread packages/controllers/src/utils/ConnectionControllerUtil.ts Outdated
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ConnectionController.setUri` has always stamped `wcPairingExpiry` four minutes
out, and scaffold-ui reads it via `isPairingExpired` to decide whether to
re-connect. The headless read never passed it through, so a host outside
scaffold-ui had no way to tell a fresh URI from one whose pairing had lapsed —
`wcUri` / `wcError` / `wcFetchingUri` read identically either way.

`getWalletConnectUri()` now returns it and `subscribeWalletConnectUri` fires on
it. Additive: existing readers are unaffected.

This is only reachable for a host that consumes the URI later than it fetched
it. Scaffold-ui generates and acts on a URI in one gesture. A headless picker
that fetches on wallet select and deeplinks on a second, user-paced click can be
well past four minutes by the time the user taps.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@0xmkh
0xmkh disabled auto-merge July 31, 2026 12:07
@0xmkh
0xmkh merged commit 4d6cf6a into main Jul 31, 2026
71 of 82 checks passed
@0xmkh
0xmkh deleted the fix/headless-wc-reconnect-after-disconnect branch July 31, 2026 12:08
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 31, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants