Description
Using matrix-js-sdk v41.4.0 in a React Native application with the Hermes JS engine, every call to MatrixRTCSession.ensureRecalculateSessionMembers() throws an unhandled TypeError: undefined is not a function at startup.
The error is silent in normal Metro output (it appears only as a sequence of opaque unhandled promise rejection IDs) and leaves the entire MatrixRTCSession layer in a permanently broken state,every room's initialMembershipCalculated promise is rejected, so RTC session membership is never computed.
Steps to reproduce
- Create a React Native app (Expo SDK 55 / bare RN 0.83.6) that uses matrix-js-sdk
41.4.0.
- Authenticate and start a Matrix client with at least one joined room.
- Call
client.matrixRTC.start() (or rely on automatic start during sync).
- Observe Metro / logcat output.
Expected: RTC sessions initialise silently.
Actual: One TypeError: undefined is not a function per room appears in the Metro log, all originating from promise/setimmediate/finally.js (React Native's internal Promise polyfill) via InternalBytecode.js.
Minimal reproduction snippet
// Minimal probe,run this anywhere after React Native initialises:
Promise.resolve()
.finally() // ← no argument
.then(() => console.log("OK"))
.catch((e) => console.error("CRASH:", e.message)); // prints "CRASH: undefined is not a function"
Root cause
MatrixRTCSession.ensureRecalculateSessionMembers() in
src/matrixrtc/MatrixRTCSession.ts contains:
this.recalculateSessionMembersPromise =
this.recalculateSessionMembersPromise
.finally() // ← called with no argument (undefined)
.then(() => this.recalculateSessionMembers());
In browsers and Node.js, Promise.prototype.finally(undefined) is a spec-compliant no-op that passes the settled value through. However, the Promise polyfill shipped with React Native (promise/setimmediate/finally.js) performs a strict typeof onFinally !== 'function' check and throws rather than treating undefined as a pass-through.
Spec reference,MDN: Promise.prototype.finally:
If onFinally is not a function, it is internally replaced with a function that simply returns the received value. This means .finally() (no argument) is equivalent to .then(x => x, x => { throw x; }) and must not throw.
React Native's polyfill diverges from this requirement.
Affected versions
| Component |
Version |
| matrix-js-sdk |
41.4.0 (regression introduced with MatrixRTC refactor) |
| React Native |
0.83.6 |
| Hermes |
bundled with RN 0.83.6 |
| Expo SDK |
55 |
promise npm package (RN built-in polyfill) |
ships with RN, promise/setimmediate/finally.js |
| Node.js (dev host) |
24.15.0 LTS |
| iOS / Android |
both affected (Hermes is default on both) |
Impact
- All React Native clients using matrix-js-sdk ≥ v41 are affected whenever at least one joined room exists.
MatrixRTCSession.initialMembershipCalculated is rejected for every room, silently disabling membership tracking for the entire RTC layer.
- No console error is produced by default,the failure is visible only via Metro's opaque rejection IDs.
Suggested fix
Replace .finally().then(fn) with .then(onFulfilled, onRejected),semantically identical, compatible with all runtimes:
- this.recalculateSessionMembersPromise = this.recalculateSessionMembersPromise
- .finally()
- .then(() => this.recalculateSessionMembers());
+ this.recalculateSessionMembersPromise = this.recalculateSessionMembersPromise.then(
+ () => this.recalculateSessionMembers(),
+ () => this.recalculateSessionMembers(),
+ );
A PR with this fix is available: #5307
Workaround (for app developers)
Until an upstream release is published, patch node_modules/matrix-js-sdk with
patch-package:
# patches/matrix-js-sdk+41.4.0.patch
- this.recalculateSessionMembersPromise = this.recalculateSessionMembersPromise.finally().then(() => this.recalculateSessionMembers());
+ this.recalculateSessionMembersPromise = this.recalculateSessionMembersPromise.then(
+ () => this.recalculateSessionMembers(),
+ () => this.recalculateSessionMembers(),
+ );
Then add to package.json:
"scripts": {
"postinstall": "patch-package"
}
Description
Using matrix-js-sdk
v41.4.0in a React Native application with the Hermes JS engine, every call toMatrixRTCSession.ensureRecalculateSessionMembers()throws an unhandledTypeError: undefined is not a functionat startup.The error is silent in normal Metro output (it appears only as a sequence of opaque unhandled promise rejection IDs) and leaves the entire
MatrixRTCSessionlayer in a permanently broken state,every room'sinitialMembershipCalculatedpromise is rejected, so RTC session membership is never computed.Steps to reproduce
41.4.0.client.matrixRTC.start()(or rely on automatic start during sync).Expected: RTC sessions initialise silently.
Actual: One
TypeError: undefined is not a functionper room appears in the Metro log, all originating frompromise/setimmediate/finally.js(React Native's internal Promise polyfill) viaInternalBytecode.js.Minimal reproduction snippet
Root cause
MatrixRTCSession.ensureRecalculateSessionMembers()insrc/matrixrtc/MatrixRTCSession.tscontains:In browsers and Node.js,
Promise.prototype.finally(undefined)is a spec-compliant no-op that passes the settled value through. However, the Promise polyfill shipped with React Native (promise/setimmediate/finally.js) performs a stricttypeof onFinally !== 'function'check and throws rather than treatingundefinedas a pass-through.Spec reference,MDN:
Promise.prototype.finally:React Native's polyfill diverges from this requirement.
Affected versions
promisenpm package (RN built-in polyfill)promise/setimmediate/finally.jsImpact
MatrixRTCSession.initialMembershipCalculatedis rejected for every room, silently disabling membership tracking for the entire RTC layer.Suggested fix
Replace
.finally().then(fn)with.then(onFulfilled, onRejected),semantically identical, compatible with all runtimes:A PR with this fix is available: #5307
Workaround (for app developers)
Until an upstream release is published, patch
node_modules/matrix-js-sdkwithpatch-package:Then add to
package.json: