Skip to content

fix: upgrade matrix-js-sdk to 45 - #10539

Draft
polibb wants to merge 1 commit into
developfrom
feat/095-matrix-sdk-45-upgrade
Draft

polibb wants to merge 1 commit into
developfrom
feat/095-matrix-sdk-45-upgrade

Conversation

@polibb

@polibb polibb commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #10538

Workspace spec: workspace#095-matrix-sdk-45-upgrade — https://github.com/alkem-io/agents-hq/tree/095-matrix-sdk-45-upgrade/specs/095-matrix-sdk-45-upgrade/

Root cause

When Synapse rejects an expired access token, matrix-js-sdk emits Session.logged_out synchronously and only then rethrows. The listener in src/core/matrix/sessionController.ts runs recover(), which stops the old client at once. In 42.2.0, stopClient() only clears the capabilities poller's timers. The rejected (or still in flight) capabilities request then lands in CapabilityPoller.poll's catch, which re-arms a 30–35 s retry. The stopped client therefore polled /capabilities with the expired token for the rest of the page's life. A request that succeeded after the stop re-armed the 6 h refresh in the same way.

matrix-js-sdk 45.0.0 gives the poller a stopped state (matrix-org/matrix-js-sdk#5604). Versions 42.3, 43 and 44 still have the race.

Change

  • matrix-js-sdk ^42.2.0 → ^45.0.0 (the lockfile resolves 45.0.0).

  • matrix-widget-api 1.19.0 → 1.20.0. 45.0.0 requires ^1.20.0, and this keeps a single copy in the bundle.

  • New src/core/matrix/sessionController.stoppedClient.test.ts drives the real session controller with the real SDK. Its fetch double holds responses in flight, so the order of events is controlled, and fake timers advance past the retry window. It has three cases:

    • the capabilities request's own 401 triggers recovery;
    • the capabilities request is in flight while /sync gets the 401, and then it fails;
    • the same, but the capabilities request then succeeds.

    Each case asserts that no request is started with the old token after recovery, and that the new client is the active one.

  • No product source change was needed.

RED/GREEN: on 42.2.0 all three cases fail on the expired-token capabilities assertion. On 45.0.0 all 3 pass.

Breaking-change audit (43 / 44 / 45)

Change client-web impact
43: OAuth token refresh moved into the SDK None. The client is created with an access token only. With no refresh token the SDK still resolves to "logout" and emits Session.logged_out before rethrowing. The regression test exercises this path.
44: deprecated MatrixRTC APIs removed None. 45 still has getRoomSession, the session_started/session_ended/memberships_changed events, memberships, and membership sender/deviceId/isExpired()/getMsUntilExpiry(). These were checked in the installed sources, because matrixRtc.ts reaches them through local shapes that typecheck cannot see.
44: "oldest membership" transport selection removed; LiveKit transports and configs merged None. client-web hands the call UI one fixed LiveKit transport, typed by matrix-widget-api, whose type is unchanged.
45: LiveKit request types use member_id (widget-api 1.20) None. getRtcLivekitToken/delegateRtcLivekitDelayedLeave take no parameters and still reject as unsupported. Typecheck passes.
45: MSC4140 parent delay IDs deprecated None. The driver passes { delay } only.
Delayed-event update on Synapse 1.132 Unchanged. _unstable_updateDelayedEvent still sends the action in the body; that code is byte-identical to 42.2.0.
Crypto WASM pinned at 18.9.0 None. client-web never initialises crypto.
Element Call (vite.element-call.mjs) None. It is a prebuilt static copy and is not bundled.

Out of scope

  • The delayed-event 401 M_MISSING_TOKEN → 200 pair per call heartbeat. This is the SDK's own unauthenticated-first fallback; an optional follow-up.
  • The media download 500 for a call avatar, which is server-side.
  • Synapse stays v1.132.0.

Verification

  • Exit gates passed in one run: pnpm vitest run (658 files / 5712 tests), pnpm build, pnpm lint. Vitest ran under LC_ALL=en_US.UTF-8, because of the known bg_BG-locale budgetMeter test.
  • SC-005, a post-deploy check on acceptance (manual, read-only): after an expired-token recovery, DevTools → Network shows no recurring 401 capabilities requests.
  • Spec process: clarify loop 2 runs (the second found nothing new); analyze loop 2 runs (the second reported zero findings).
  • The new test's comments carry no spec or issue references.

After a rejected access token, session recovery stops the old Matrix
client while its capabilities request is still in flight. In 42.2.0 the
capabilities poller's error path re-arms a 30-35 s retry after stop(),
so the stopped client polled /capabilities with the expired token for
the rest of the page's life. 45.0.0 gives the poller a stopped state
(matrix-org/matrix-js-sdk#5604).

- matrix-js-sdk ^42.2.0 -> ^45.0.0 (lock 45.0.0)
- matrix-widget-api 1.19.0 -> 1.20.0, the minimum 45 requires, so one
  copy is bundled
- regression test drives the real session controller and SDK against a
  fetch double that holds responses in flight, then advances past the
  retry window

RED on 42.2.0: all three cases fail on the expired-token capabilities
assertion. GREEN on 45.0.0: 3/3 pass.
@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: stopped Matrix client keeps polling capabilities with an expired token

1 participant