Skip to content

fix(calling): don't fail startCall when reaction listener is blocked … - #6065

Merged
bluk16 merged 3 commits into
mainfrom
fix/reaction-listener-policy-startcall
Jul 27, 2026
Merged

fix(calling): don't fail startCall when reaction listener is blocked …#6065
bluk16 merged 3 commits into
mainfrom
fix/reaction-listener-policy-startcall

Conversation

@bluk16

@bluk16 bluk16 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

From Icm: https://portal.microsofticm.com/imp/v5/incidents/details/21000001115074/summary
startCall (and join) can throw and return a null Call when the app has no involvement with reactions at all, because the stateful client unconditionally registers a 'reaction' listener during call wiring. On calls where that registration is policy-gated by the Calling SDK — notably Teams-identity outbound Group / PSTN calls — the SDK throws an ExpectedError (code=403 subCode=45802 EVENT_SUBSCRIBE_FAIL_POLICY, "Unable to register listener due to meeting policy"). That error propagates out of CallAgent.startCall, aborting call setup even though the application never uses reactions.

This makes reactions — an optional feature — a hard dependency of establishing a call.

Root cause

CallSubscriber's constructor unconditionally constructs a ReactionSubscriber:

this._reactionSubscriber = new ReactionSubscriber(
  this._callIdRef,
  this._context,
  this._call.feature(Features.Reaction)
);

ReactionSubscriber's constructor synchronously calls this._reaction.on('reaction', …). When that .on() is rejected by meeting policy, the exception is thrown during CallSubscriber construction, which happens as part of startCall / join. Nothing catches it, so it surfaces to the caller and the returned Call is null.

Other optional-feature subscribers in this file (captions, real-time text, local recording) are already guarded against exactly this class of failure via the _safeSubscribe helper; the reaction subscriber was missing that guard.

Fix

Wrap the ReactionSubscriber construction in the existing _safeSubscribe(...) helper, which try/catches the subscription and tees any error to state via teeErrorToState instead of letting it abort call setup:

this._safeSubscribe(() => {
  this._reactionSubscriber = new ReactionSubscriber(
    this._callIdRef,
    this._context,
    this._call.feature(Features.Reaction)
  );
});

This mirrors the pattern already used for the other policy-gated optional-feature subscribers, so the behavior is consistent: a blocked reaction subscription no longer breaks a call, and the failure is still observable through latestErrors rather than being swallowed silently.

Testing

  • Added a regression test: "should not fail call setup when registering the reaction listener is rejected by meeting policy". It makes the mocked Reaction feature's .on() throw the 403 / EVENT_SUBSCRIBE_FAIL_POLICY error and asserts the call is still set up in state (and the error is tee'd to latestErrors['Call.on']).
  • Verified the guard is real: reverting only the _safeSubscribe wrap makes exactly this one test fail; with the fix the full calling-stateful-client suite passes (70/70).

Change type

Bug fix (patch). Change files added for both the stable and beta channels.

…by meeting policy

The stateful client's CallSubscriber unconditionally creates a
ReactionSubscriber for every call, which synchronously registers a
'reaction' listener via ReactionCallFeature.on(). For Teams-identity
outbound group/PSTN calls the Calling SDK rejects this registration with
an ExpectedError (code=403 subCode=45802 EVENT_SUBSCRIBE_FAIL_POLICY,
"Unable to register listener due to meeting policy").

Because the construction was not guarded, the exception propagated out of
CallAgent.startCall / join, so the returned Call/TeamsCall was null even
though the app never uses reactions. Toggling the Teams meeting-policy
reactions setting has no effect because these are group/PSTN calls, not
meetings, and the subscription happens unconditionally.

Wrap the ReactionSubscriber creation in the existing _safeSubscribe helper
(purpose-built to stop a throwing subscriber from breaking call setup) so
the expected policy error is tee'd to state instead of aborting the call.

Adds a regression test asserting the call is still set up when the reaction
listener registration throws 403/45802.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@azure/communication-react jest test coverage for beta.

Lines Statements Functions Branches
Base 58580 / 95109
61.59%
58580 / 95109
61.59%
1180 / 2709
43.55%
3417 / 5799
58.92%
Current 58631 / 95274
61.53%
58631 / 95274
61.53%
1185 / 2719
43.58%
3408 / 5809
58.66%
Diff 51 / 165
-0.06%
51 / 165
-0.06%
5 / 10
0.03%
-9 / 10
-0.26%

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@azure/communication-react jest test coverage for stable.

Lines Statements Functions Branches
Base 29739 / 47381
62.76%
29739 / 47381
62.76%
810 / 1491
54.32%
2394 / 3842
62.31%
Current 29732 / 47501
62.59%
29732 / 47501
62.59%
815 / 1501
54.29%
2432 / 3868
62.87%
Diff -7 / 120
-0.17%
-7 / 120
-0.17%
5 / 10
-0.03%
38 / 26
0.56%

Copilot AI requested review from a team as code owners July 23, 2026 13:18
Copilot AI requested review from juancamilor and pohtsng July 23, 2026 13:18
@bluk16
bluk16 enabled auto-merge (squash) July 27, 2026 13:19
@github-actions

Copy link
Copy Markdown
Contributor

📦 Chat bundle size

Bundle Base Size (bytes) Current Size (bytes) Change Delta (bytes)
build.bundle.js 1793916 1794311 ⚠️ increased 395
398.bundle.js 375829 375829 ➖ unchanged 0
450.bundle.js 4454 4454 ➖ unchanged 0
82.bundle.js 28161 28139 ⬇️ decreased -22
91.bundle.js 4102 4102 ➖ unchanged 0

Total change: +373 B ⚠️

@github-actions

Copy link
Copy Markdown
Contributor

📦 Calling bundle size

Bundle Base Size (bytes) Current Size (bytes) Change Delta (bytes)
build.bundle.js 6767681 6769999 ⚠️ increased 2318
580.bundle.js 12407925 12407925 ➖ unchanged 0

Total change: +2.3 KB ⚠️

@github-actions

Copy link
Copy Markdown
Contributor

📦 CallWithChat bundle size

Bundle Base Size (bytes) Current Size (bytes) Change Delta (bytes)
build.bundle.js 7758336 7760699 ⚠️ increased 2363
450.bundle.js 4470 4470 ➖ unchanged 0
580.bundle.js 12407938 12407938 ➖ unchanged 0
727.bundle.js 372564 372564 ➖ unchanged 0
82.bundle.js 28177 28155 ⬇️ decreased -22
91.bundle.js 4118 4118 ➖ unchanged 0

Total change: +2.3 KB ⚠️

@bluk16
bluk16 merged commit dc88af5 into main Jul 27, 2026
40 checks passed
@bluk16
bluk16 deleted the fix/reaction-listener-policy-startcall branch July 27, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants