Skip to content

fix(llc): improve the handling of SFU join errors#1191

Merged
Brazol merged 7 commits intomainfrom
fix/full-sfu-join-erros
Mar 17, 2026
Merged

fix(llc): improve the handling of SFU join errors#1191
Brazol merged 7 commits intomainfrom
fix/full-sfu-join-erros

Conversation

@Brazol
Copy link
Contributor

@Brazol Brazol commented Mar 17, 2026

Some SFU errors are preventing the join flow. The common case is SFU_FULL, which technically shouldn't be retried, as the SFU will likely reject the second attempt too. In this case, we immediately ask the coordinator for a brand new SFU, hopefully with enough capacity.

Summary by CodeRabbit

  • New Features

    • Improved SFU migration and join logic to reduce failed joins and reconnect loops
    • Client WebRTC version reporting for richer diagnostics
    • Added Python and Vision Agents SDK type support
    • Support for passing multi-source migration context to coordinator joins
  • Bug Fixes

    • Better handling of SFU capacity and shutting-down cases to improve recovery
    • More robust classification of SFU join errors to avoid unnecessary retries
  • Tests

    • Updated tests and fixtures to cover multi-source migration handling
  • Chores

    • Increased Android Gradle daemon heap for example project

@Brazol Brazol requested a review from a team as a code owner March 17, 2026 12:00
@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Bumps protocolModelsVersion, adds migratingFromList to coordinator join flows, adds webrtcVersion to ClientDetails, extends SFU join/retry/migration and exclusion logic, adds two SdkType enum values, adjusts WebRTC track handling, updates tests/fixtures, changelog, and example Gradle heap.

Changes

Cohort / File(s) Summary
Protocol & Protobuf
packages/stream_video/lib/globals.dart, packages/stream_video/lib/protobuf/.../models.pb.dart, packages/stream_video/lib/protobuf/.../models.pbenum.dart, packages/stream_video/lib/protobuf/.../models.pbjson.dart
Bumped protocolModelsVersion to 1.46.1; added webrtcVersion field to ClientDetails; added SDK_TYPE_PYTHON and SDK_TYPE_VISION_AGENTS enum values.
Coordinator API & Models
packages/stream_video/lib/open_api/video/coordinator/model/join_call_request.dart, packages/stream_video/lib/src/coordinator/coordinator_client.dart, packages/stream_video/lib/src/coordinator/open_api/coordinator_client_open_api.dart, packages/stream_video/lib/src/coordinator/retry/coordinator_client_retry.dart
Added migratingFromList: List<String> to JoinCallRequest and propagated the parameter through coordinator client APIs and retry wrapper (default const []).
Call & SFU Logic
packages/stream_video/lib/src/call/call.dart, packages/stream_video/lib/src/sfu/data/models/sfu_error.dart
Introduced per-call SFU exclusion (sfusToExclude), added SFU error classification helpers and migration reasoning (populating migratingFromList), and added SfuErrorCode.isJoinErrorCode.
Client Details & WebRTC
packages/stream_video/lib/src/stream_video.dart
Detects and populates webrtcVersion into ClientDetails sent to SFU.
WebRTC Track Management
packages/stream_video/lib/src/webrtc/rtc_manager.dart
Refactored publishAudioTrack/publishVideoTrack to use local audio/video track aliases to avoid mutating original track references.
Tests & Fixtures
packages/stream_video/test/src/call/call_allow_multiple_active_calls_test.dart, packages/stream_video/test/src/call/call_test.dart, packages/stream_video/test/src/call/fixtures/call_test_helpers.dart, packages/stream_video/test/src/call/call_join_sfu_error_test.dart
Updated mocks and expectations to include migratingFromList; added extensive SFU join error handling tests and adjusted fixtures.
Gradle Example
packages/stream_video_flutter/example/android/gradle.properties
Increased Gradle daemon max heap from -Xmx2000M to -Xmx4096M.
Changelog
packages/stream_video/CHANGELOG.md
Added Upcoming note describing improved SFU join, recovery, and migration logic.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client SDK
  participant Coordinator as Coordinator Service
  participant SFU as SFU Server

  Client->>Coordinator: joinCall(callCid, migratingFromList)
  Coordinator-->>Client: CoordinatorJoined (selected SFU)
  Client->>SFU: join request (client details incl. webrtcVersion)
  SFU-->>Client: join response (success) or error (e.g., sfuFull)
  alt SFU join success
    Client->>Client: proceed with in-call setup
  else SFU join error (join-error)
    Client->>Client: add SFU to sfusToExclude
    Client->>Coordinator: joinCall(callCid, migratingFromList + currentSfu)
    Coordinator-->>Client: CoordinatorJoined (alternative SFU)
    Client->>SFU: join request to alternative SFU
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐇 I hop from SFU to SFU with a careful list,

I carry versions and names I mustn't miss.
When one is full, I skip and try the rest,
A migrating trail helps me choose the best.
Hooray — smoother joins and steadier bliss.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is missing critical sections from the template including Goal, Implementation details, UI Changes, Testing, and Contributor Checklist. Complete the description by adding the required sections: Goal (explaining why this change is needed), Implementation details (describing the technical approach), Testing (how to validate the fix), and filling out the Contributor Checklist.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: improving the handling of SFU join errors, which is the primary objective of this changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/full-sfu-join-erros
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Brazol Brazol linked an issue Mar 17, 2026 that may be closed by this pull request
@Brazol Brazol changed the title fix(llc): improve the handling of join errors and prevent cross-socket event leaking fix(llc): improve the handling of SFU join errors Mar 17, 2026
@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 33.33333% with 54 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.42%. Comparing base (862a4b5) to head (d12514c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...kages/stream_video/lib/src/webrtc/rtc_manager.dart 0.00% 22 Missing ⚠️
packages/stream_video/lib/src/call/call.dart 63.88% 13 Missing ⚠️
...video/lib/protobuf/video/sfu/models/models.pb.dart 0.00% 10 Missing ⚠️
...api/video/coordinator/model/join_call_request.dart 0.00% 8 Missing ⚠️
...o/lib/protobuf/video/sfu/models/models.pbenum.dart 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #1191      +/-   ##
========================================
+ Coverage   6.36%   6.42%   +0.06%     
========================================
  Files        615     615              
  Lines      43362   43412      +50     
========================================
+ Hits        2758    2788      +30     
- Misses     40604   40624      +20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stream_video/lib/src/coordinator/retry/coordinator_client_retry.dart (1)

282-289: ⚠️ Potential issue | 🟠 Major

Forward membersLimit in retry join delegation (currently dropped).

Line 279 accepts membersLimit, but the delegate call omits it. This can change join response shape when using CoordinatorClientRetry.

🔧 Proposed fix
       () => _delegate.joinCall(
         callCid: callCid,
         ringing: ringing,
         create: create,
         migratingFrom: migratingFrom,
         migratingFromList: migratingFromList,
         video: video,
+        membersLimit: membersLimit,
       ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/stream_video/lib/src/coordinator/retry/coordinator_client_retry.dart`
around lines 282 - 289, The retry wrapper is dropping the membersLimit parameter
when delegating to the underlying client; update the closure passed to
_delegate.joinCall inside CoordinatorClientRetry so it forwards the membersLimit
argument (membersLimit: membersLimit) along with callCid, ringing, create,
migratingFrom, migratingFromList, and video, ensuring the delegate receives the
same membersLimit value and the join response shape remains consistent.
🧹 Nitpick comments (2)
packages/stream_video/lib/src/webrtc/rtc_manager.dart (1)

738-739: Rollback provisional tracks state on publish failures

Line [738] and Line [815] register a local track before publish is confirmed. If an error returns at Line [760], Line [826], or Line [848], tracks can retain a non-published track, which can mislead later flows (getPublisherTrackByType, mute/unmute, device switching).

Proposed fix
@@
-    tracks[audioTrack.trackId] = audioTrack;
+    tracks[audioTrack.trackId] = audioTrack;
+    var publishedAny = false;
@@
         if (transceiverResult is Failure) {
           await mediaTrackClone.stop();
+          if (!publishedAny) tracks.remove(audioTrack.trackId);
           return transceiverResult;
         }
+        publishedAny = true;
@@
-    tracks[videoTrack.trackId] = videoTrack;
+    tracks[videoTrack.trackId] = videoTrack;
+    var publishedAny = false;
@@
     if (!publishOptions.any((o) => o.trackType == videoTrack.trackType)) {
+      tracks.remove(videoTrack.trackId);
       _logger.w(
@@
         if (transceiverResult is Failure) {
           await mediaTrackClone.stop();
+          if (!publishedAny) tracks.remove(videoTrack.trackId);
           return transceiverResult;
         }
+        publishedAny = true;

Also applies to: 760-762, 815-816, 826-828, 848-850

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_video/lib/src/webrtc/rtc_manager.dart` around lines 738 -
739, The provisional insertion of a local track into the tracks map
(tracks[audioTrack.trackId] = audioTrack / tracks[videoTrack.trackId] =
videoTrack and their updatedTrack variants) must be rolled back if publish
fails; update the publish flow in the methods that register these provisional
tracks (references: tracks, audioTrack.trackId, videoTrack.trackId,
updatedTrack) to remove the entry from tracks when an error path is taken (the
failure return points around the current lines and the other error locations
mentioned) so the tracks map only contains actually published tracks—i.e., after
setting tracks[...] = ..., attempt the publish, and on any exception or
non-success response call tracks.remove(audioTrack.trackId) /
tracks.remove(videoTrack.trackId) (and ensure you don't remove an already
replaced/persisted track) before returning the error.
packages/stream_video/test/src/call/call_test.dart (1)

67-75: Strengthen migratingFromList assertions to validate behavior, not just presence.

Using any(named: 'migratingFromList') won’t catch regressions in exclusion-list construction. Assert concrete values for first join ([]) and migration join (contains previous SFU).

✅ Suggested test tightening
-            migratingFromList: any(named: 'migratingFromList'),
+            migratingFromList: <String>[],

...
-            migratingFromList: any(named: 'migratingFromList'),
+            migratingFromList: <String>[
+              SampleCallData.defaultCredentials.sfuServer.name,
+            ],

...
-            migratingFromList: any(named: 'migratingFromList'),
+            migratingFromList: <String>[],

Also applies to: 94-102, 141-149

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_video/test/src/call/call_test.dart` around lines 67 - 75, The
test currently uses any(named: 'migratingFromList') when verifying
coordinatorClient.joinCall, which only checks presence and can miss regressions;
change the expectations to assert concrete migratingFromList values: for a first
join assert an empty list ([]) is passed to migratingFromList, and for migration
join assertions assert that migratingFromList contains the previous SFU
identifier (or the exact list expected), updating the verifier calls around
coordinatorClient.joinCall and any other similar expectations (the other
joinCall assertions in the test file) to check the exact migratingFromList
contents instead of any(...).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stream_video/CHANGELOG.md`:
- Line 4: Update the release note text in the CHANGELOG entry that currently
reads "Improved SFU join, recovery and migration logic to reduce failed joins
with reconnec loops when joining full or shutting down SFU" by fixing the typo
"reconnec loops" to "reconnection loops" (or "reconnect loops" if preferred) so
the user-facing changelog reads correctly; locate the line containing the phrase
"reconnec loops" and replace it with "reconnection loops".

In
`@packages/stream_video/lib/open_api/video/coordinator/model/join_call_request.dart`:
- Line 101: The equality uses deep list comparison for migratingFromList but the
hashCode uses the list's identity hash, breaking the equality/hashCode contract;
update the JoinCallRequest (or the class defining == and hashCode) to compute
the hash for migratingFromList using _deepEquality.hash(migratingFromList)
instead of migratingFromList.hashCode so equal lists produce the same hash;
ensure the symbol names to change are the equality check referencing
_deepEquality.equals(other.migratingFromList, migratingFromList) and the
hashCode implementation that currently uses migratingFromList.hashCode.

---

Outside diff comments:
In
`@packages/stream_video/lib/src/coordinator/retry/coordinator_client_retry.dart`:
- Around line 282-289: The retry wrapper is dropping the membersLimit parameter
when delegating to the underlying client; update the closure passed to
_delegate.joinCall inside CoordinatorClientRetry so it forwards the membersLimit
argument (membersLimit: membersLimit) along with callCid, ringing, create,
migratingFrom, migratingFromList, and video, ensuring the delegate receives the
same membersLimit value and the join response shape remains consistent.

---

Nitpick comments:
In `@packages/stream_video/lib/src/webrtc/rtc_manager.dart`:
- Around line 738-739: The provisional insertion of a local track into the
tracks map (tracks[audioTrack.trackId] = audioTrack / tracks[videoTrack.trackId]
= videoTrack and their updatedTrack variants) must be rolled back if publish
fails; update the publish flow in the methods that register these provisional
tracks (references: tracks, audioTrack.trackId, videoTrack.trackId,
updatedTrack) to remove the entry from tracks when an error path is taken (the
failure return points around the current lines and the other error locations
mentioned) so the tracks map only contains actually published tracks—i.e., after
setting tracks[...] = ..., attempt the publish, and on any exception or
non-success response call tracks.remove(audioTrack.trackId) /
tracks.remove(videoTrack.trackId) (and ensure you don't remove an already
replaced/persisted track) before returning the error.

In `@packages/stream_video/test/src/call/call_test.dart`:
- Around line 67-75: The test currently uses any(named: 'migratingFromList')
when verifying coordinatorClient.joinCall, which only checks presence and can
miss regressions; change the expectations to assert concrete migratingFromList
values: for a first join assert an empty list ([]) is passed to
migratingFromList, and for migration join assertions assert that
migratingFromList contains the previous SFU identifier (or the exact list
expected), updating the verifier calls around coordinatorClient.joinCall and any
other similar expectations (the other joinCall assertions in the test file) to
check the exact migratingFromList contents instead of any(...).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc7822cf-879a-4a14-819f-4cee12f22d65

📥 Commits

Reviewing files that changed from the base of the PR and between 45519e3 and 3ed5b16.

📒 Files selected for processing (16)
  • packages/stream_video/CHANGELOG.md
  • packages/stream_video/lib/globals.dart
  • packages/stream_video/lib/open_api/video/coordinator/model/join_call_request.dart
  • packages/stream_video/lib/protobuf/video/sfu/models/models.pb.dart
  • packages/stream_video/lib/protobuf/video/sfu/models/models.pbenum.dart
  • packages/stream_video/lib/protobuf/video/sfu/models/models.pbjson.dart
  • packages/stream_video/lib/src/call/call.dart
  • packages/stream_video/lib/src/coordinator/coordinator_client.dart
  • packages/stream_video/lib/src/coordinator/open_api/coordinator_client_open_api.dart
  • packages/stream_video/lib/src/coordinator/retry/coordinator_client_retry.dart
  • packages/stream_video/lib/src/sfu/data/models/sfu_error.dart
  • packages/stream_video/lib/src/stream_video.dart
  • packages/stream_video/lib/src/webrtc/rtc_manager.dart
  • packages/stream_video/test/src/call/call_allow_multiple_active_calls_test.dart
  • packages/stream_video/test/src/call/call_test.dart
  • packages/stream_video/test/src/call/fixtures/call_test_helpers.dart

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/stream_video/CHANGELOG.md (1)

4-4: Consider adding detail about the SFU exclusion mechanism.

While the current description is accurate, users might benefit from understanding that the improvement works by excluding problematic SFUs when requesting a new one from the coordinator. This aligns with the migratingFromList feature documented in the code.

💡 Optional enhancement for clarity
-* Improved SFU join, recovery and migration logic to reduce failed joins with reconnect loops when joining full or shutting-down SFUs
+* Improved SFU join, recovery and migration logic to reduce failed joins with reconnect loops when joining full or shutting-down SFUs by requesting alternative SFUs from the coordinator
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_video/CHANGELOG.md` at line 4, The changelog line is vague
about how SFU join/recovery improvements work; update the entry to mention the
SFU exclusion mechanism by describing that the client/agent now supplies a
migratingFromList (or similar exclusion list) to the coordinator so problematic
SFUs are excluded when requesting a new SFU, reducing reconnect loops and failed
joins during migration or when SFUs are full/shutting down; reference the
migratingFromList feature in the description for clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stream_video/CHANGELOG.md`:
- Line 4: Update the CHANGELOG entry bullet (the line starting with "* Improved
SFU join, recovery and migration logic...") by removing the trailing whitespace
and rephrasing for clarity and grammar; for example, add the missing serial
comma after "recovery", pluralize "SFU" to "SFUs" and clarify the outcome by
changing "...to reduce failed joins with reconnect loops when joining full or
shutting-down SFU" to something like "...to reduce failed joins and reconnect
loops when joining full or shutting-down SFUs". Ensure there is no trailing
space at the end of the line.

In `@packages/stream_video/lib/src/call/call.dart`:
- Around line 936-941: The unrecoverable SFU error branch in _join currently
logs and returns early via the _isUnrecoverableSfuError check, which skips the
normal disconnect/cleanup; change the branch in _join so it invokes the same
teardown/cleanup path (e.g., call the existing disconnect/cleanup method used
elsewhere in this class such as the cleanup/disconnect/teardown helper) before
returning and ensure the call state is set to closed/failed (not left in
reconnecting). Locate the _isUnrecoverableSfuError check and add a call to the
class's disconnect/teardown routine and state update immediately after logging
and before returning.

---

Nitpick comments:
In `@packages/stream_video/CHANGELOG.md`:
- Line 4: The changelog line is vague about how SFU join/recovery improvements
work; update the entry to mention the SFU exclusion mechanism by describing that
the client/agent now supplies a migratingFromList (or similar exclusion list) to
the coordinator so problematic SFUs are excluded when requesting a new SFU,
reducing reconnect loops and failed joins during migration or when SFUs are
full/shutting down; reference the migratingFromList feature in the description
for clarity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a31f881a-0ece-402a-a30c-6b801637154c

📥 Commits

Reviewing files that changed from the base of the PR and between 3ed5b16 and c6b6f9e.

📒 Files selected for processing (3)
  • packages/stream_video/CHANGELOG.md
  • packages/stream_video/lib/src/call/call.dart
  • packages/stream_video/lib/src/stream_video.dart

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stream_video/lib/src/call/call.dart`:
- Around line 936-943: The _join flow currently calls leave() when
_isUnrecoverableSfuError(connectionFailure) is true, but that triggers _clear()
which cancels the in-flight _idConnect and causes join() to report "connect
cancelled" instead of the original SFU error; change _join so it does NOT call
leave() in this unrecoverable-SFU branch—instead log the unrecoverable SFU error
via _logger.e and propagate/return the original failure result (or rethrow) so
_idConnect and join() surface the true SFU error; keep leave() behavior for
other disconnect reasons and avoid invoking _clear() from this branch.
- Around line 1243-1255: Normalize forceMigratingFrom by treating empty or
whitespace-only strings as null before using it to compute migratingFrom and
building effectiveMigratingFromList: replace the direct use of
forceMigratingFrom in the migratingFrom assignment with a normalized value
(e.g., null if forceMigratingFrom is null or trim().isEmpty) and ensure
migratingFromList is filtered to exclude empty/whitespace entries before
spreading into effectiveMigratingFromList so no empty string is added to the
coordinator exclusion list; refer to forceMigratingFrom, migratingFrom,
migratingFromList, effectiveMigratingFromList, _reconnectStrategy and
_session?.config.sfuName to locate the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 252121cd-1816-4163-a32d-0eecd41a650e

📥 Commits

Reviewing files that changed from the base of the PR and between fc71da3 and e578161.

📒 Files selected for processing (1)
  • packages/stream_video/lib/src/call/call.dart

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/stream_video/test/src/call/call_join_sfu_error_test.dart (2)

147-199: Consider explicitly verifying migratingFromList values.

The test uses any(named: 'migratingFromList') for all calls, but verifying the actual values would ensure the SFU exclusion list is properly accumulated across retry attempts. According to the PR objectives, the list should grow as SFUs are excluded.

💡 Suggested improvement
             verifyInOrder([
               () => localCoordinator.joinCall(
                 callCid: any(named: 'callCid'),
                 create: any(named: 'create'),
                 migratingFrom: null,
-                migratingFromList: any(named: 'migratingFromList'),
+                migratingFromList: [],
                 video: any(named: 'video'),
                 membersLimit: any(named: 'membersLimit'),
               ),
               // ... first start call ...
               () => localCoordinator.joinCall(
                 callCid: any(named: 'callCid'),
                 create: any(named: 'create'),
                 migratingFrom: SampleCallData.defaultCredentials.sfuServer.name,
-                migratingFromList: any(named: 'migratingFromList'),
+                migratingFromList: [SampleCallData.defaultCredentials.sfuServer.name],
                 video: any(named: 'video'),
                 membersLimit: any(named: 'membersLimit'),
               ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_video/test/src/call/call_join_sfu_error_test.dart` around
lines 147 - 199, The test should assert concrete migratingFromList values
instead of any(...) so we can confirm the SFU exclusion list grows across
retries: update the verifyInOrder expectations for localCoordinator.joinCall to
check migratingFromList equals an empty list on the first call, equals a list
containing the first excluded SFU on the second call, and equals a list
containing both previously excluded SFUs on the third call (use the actual
variables/constants used to represent excluded SFU names in the test), while
keeping other argument matchers the same for localCoordinator.joinCall and
localSession.start.

142-142: Consider verifying the join result.

The test calls await call.join() but discards the result. Capturing and asserting on it would strengthen the test by confirming the expected failure state.

💡 Suggested improvement
-            await call.join();
+            final result = await call.join();
+
+            expect(result.isFailure, isTrue);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream_video/test/src/call/call_join_sfu_error_test.dart` at line
142, Capture the result of call.join() (e.g., assign to a variable like
joinResult) and add assertions against its expected failure state instead of
discarding it; locate the call to call.join() in the test (call.join()) and
assert properties on the returned value (for example that it indicates failure,
contains the expected error code/message, or that its success flag is false) to
make the test verify the join outcome.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/stream_video/test/src/call/call_join_sfu_error_test.dart`:
- Around line 147-199: The test should assert concrete migratingFromList values
instead of any(...) so we can confirm the SFU exclusion list grows across
retries: update the verifyInOrder expectations for localCoordinator.joinCall to
check migratingFromList equals an empty list on the first call, equals a list
containing the first excluded SFU on the second call, and equals a list
containing both previously excluded SFUs on the third call (use the actual
variables/constants used to represent excluded SFU names in the test), while
keeping other argument matchers the same for localCoordinator.joinCall and
localSession.start.
- Line 142: Capture the result of call.join() (e.g., assign to a variable like
joinResult) and add assertions against its expected failure state instead of
discarding it; locate the call to call.join() in the test (call.join()) and
assert properties on the returned value (for example that it indicates failure,
contains the expected error code/message, or that its success flag is false) to
make the test verify the join outcome.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 429929ba-ad42-4e6d-9189-de98bda5be3d

📥 Commits

Reviewing files that changed from the base of the PR and between e578161 and d12514c.

📒 Files selected for processing (1)
  • packages/stream_video/test/src/call/call_join_sfu_error_test.dart

@Brazol Brazol merged commit 8e9bc0d into main Mar 17, 2026
14 checks passed
@Brazol Brazol deleted the fix/full-sfu-join-erros branch March 17, 2026 14:21
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.

Multiple sequential reconnections impeding users to stay in call

2 participants