Skip to content

Conversation

@thisisamir98
Copy link
Collaborator

@thisisamir98 thisisamir98 commented Oct 16, 2025

BugWPB-21122 [Web] Recover from having a local unestablished MLS group

Description

Problem scenario

If a client fails to establish a MLS conversation it might be left with a local MLS group in core crypto. Since this MLS group was never established it will be stuck epoch 0.

Since conversationExists()will return true clients will consider this conversation to be established even though it’s not.

Recovery

When trying to send a message in a conversation with a local MLS group stuck at epoch 0, delete the local MLS group and re-join the group / establish the group.

@thisisamir98 thisisamir98 requested review from a team and otto-the-bot as code owners October 16, 2025 13:24
@thisisamir98 thisisamir98 requested review from Copilot and typfel and removed request for a team and otto-the-bot October 16, 2025 13:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR addresses a recovery mechanism for MLS conversations that fail to establish properly, leaving a local MLS group stuck at epoch 0. The changes enable the system to detect this failure state and attempt recovery by wiping the local MLS group and re-establishing it using the correct epoch from the backend.

Key Changes:

  • Added detection logic for MLS groups stuck at epoch 0 during message sending
  • Implemented recovery mechanism that wipes local MLS group and re-establishes with backend epoch
  • Refactored conversation establishment logic into separate methods for better maintainability

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@thisisamir98 thisisamir98 changed the title fix: Recover from having a local unestablished MLS group [WPB-21112] fix: Recover from having a local unestablished MLS group [WPB-21122] Oct 16, 2025
@sonarqubecloud
Copy link

@codecov
Copy link

codecov bot commented Oct 16, 2025

Codecov Report

❌ Patch coverage is 7.69231% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.62%. Comparing base (09ea856) to head (d1c83af).
⚠️ Report is 4 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #19679      +/-   ##
==========================================
- Coverage   43.65%   43.62%   -0.03%     
==========================================
  Files        1294     1294              
  Lines       32413    32440      +27     
  Branches     7198     7205       +7     
==========================================
+ Hits        14149    14153       +4     
- Misses      16565    16589      +24     
+ Partials     1699     1698       -1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thisisamir98 thisisamir98 requested a review from typfel October 16, 2025 13:43
Copy link
Contributor

@e-maad e-maad left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@github-actions
Copy link
Contributor

🔗 Download Full Report Artifact

🧪 Playwright Test Summary

  • Passed: 8
  • Failed: 5
  • Skipped: 0
  • 🔁 Flaky: 1
  • 📊 Total: 14
  • Total Runtime: 1079.9s (~ 17 min 0 sec)

Failed Tests:

❌ Channels Management (tags: TC-8752, crit-flow-web)

Location: specs/CriticalFlow/channelsManagement-TC-8752.spec.ts:36
Duration: 74234ms

Errors:

TimeoutError: locator.waitFor: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="item-message"] .message-body:not(:has(p.text-foreground)):has(.text)').last() to be visible


   at pageManager/webapp/pages/conversation.page.ts:160

  158 |     if (waitForVisibility) {
  159 |       // Wait for the last message to be visible
> 160 |       await this.messages.last().waitFor({state: 'visible', timeout: 20_000});
      |                                  ^
  161 |     }
  162 |
  163 |     // Then get all matching elements
    at ConversationPage.isMessageVisible (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversation.page.ts:160:34)
    at sendTextMessageToConversation (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/utils/userActions.ts:85:37)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/channelsManagement-TC-8752.spec.ts:158:5
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/channelsManagement-TC-8752.spec.ts:157:3
❌ Planning group call with sending various messages during call (tags: TC-8632, crit-flow-web)

Location: specs/CriticalFlow/groupCalls-TC-8632.spec.ts:37
Duration: 24434ms

Errors:

Error: expect(received).toBeFalsy()

Received: true

  125 |     await test.step('Member unmutes themselves', async () => {
  126 |       await memberCalling.unmuteSelfInFullScreen();
> 127 |       expect(await memberCalling.isSelfUserMutedInFullScreen()).toBeFalsy();
      |                                                                 ^
  128 |     });
  129 |
  130 |     await test.step('Validation: Owner sees member is unmuted', async () => {
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupCalls-TC-8632.spec.ts:127:65
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupCalls-TC-8632.spec.ts:125:5
❌ Group Video call (tags: TC-8637, crit-flow-web)

Location: specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:39
Duration: 52239ms

Errors:

TimeoutError: locator.click: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="item-call"]').locator('[data-uie-name="do-call-controls-call-accept"]')


   at pageManager/webapp/pages/calling.page.ts:66

  64 |
  65 |   async clickAcceptCallButton() {
> 66 |     await this.acceptCallButton.click();
     |                                 ^
  67 |   }
  68 |
  69 |   async clickToggleVideoButton() {
    at CallingPage.clickAcceptCallButton (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/calling.page.ts:66:33)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:147:34
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/groupVideoCall-TC-8637.spec.ts:145:16
❌ New person joins team and setups up device (tags: TC-8635, crit-flow-web)

Location: specs/CriticalFlow/joinTeam-TC-8635.spec.ts:37
Duration: 49031ms

Errors:

Error: expect(locator).toBeVisible() failed

Locator: getByText('@Marisa Boyle')
Expected: visible
Error: strict mode violation: getByText('@Marisa Boyle') resolved to 2 elements:
    1) <span data-uie-name="secondary-line" class="conversation-list-cell-description conversation-list-cell-description--active">Whitney Hartmann: @Marisa Boyle</span> aka getByRole('button', { name: 'Open profile of Crits' })
    2) <span role="button" tabindex="-1" data-uie-name="label-self-mention" class="message-mention self-mention">…</span> aka getByTestId('label-self-mention')

Call log:
  - Expect "toBeVisible" with timeout 10000ms
  - waiting for getByText('@Marisa Boyle')


  151 |
  152 |     await test.step('A sees the mention in the chat', async () => {
> 153 |       await expect(pages.conversation().page.getByText(`@${memberA.fullName}`)).toBeVisible();
      |                                                                                 ^
  154 |     });
  155 |   },
  156 | );
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/joinTeam-TC-8635.spec.ts:153:81
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/joinTeam-TC-8635.spec.ts:152:16
❌ Messages in Groups (tags: TC-8751, crit-flow-web)

Location: specs/CriticalFlow/messagesInGroups-TC-8751.spec.ts:42
Duration: 43638ms

Errors:

TimeoutError: locator.waitFor: Timeout 20000ms exceeded.
Call log:
  - waiting for locator('[data-uie-name="item-message"] [data-uie-name="message-reactions"] button[data-uie-name="emoji-pill"][aria-label="1 reaction, react with +1 emoji"]').first() to be visible


   at pageManager/webapp/pages/conversation.page.ts:210

  208 |
  209 |     // Wait for at least one matching element to appear (optional timeout can be set)
> 210 |     await plusOneReactionIcon.first().waitFor({state: 'visible'});
      |                                       ^
  211 |
  212 |     return await plusOneReactionIcon.isVisible();
  213 |   }
    at ConversationPage.isPlusOneReactionVisible (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversation.page.ts:210:39)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInGroups-TC-8751.spec.ts:133:46
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/messagesInGroups-TC-8751.spec.ts:129:5

Flaky Tests:

⚠️ Setting up new device with a backup (tags: TC-8634, crit-flow-web)

Location: specs/CriticalFlow/backupRestoration-TC-8634.spec.ts:35

Attempt 1
Result: ❌ Failed
Duration: 39368ms

Errors:

Error: locator.waitFor: Test ended.
Call log:
  - waiting for locator('[data-uie-name="item-message"] .message-body:not(:has(p.text-foreground)):has(.text)').last() to be visible


   at pageManager/webapp/pages/conversation.page.ts:160

  158 |     if (waitForVisibility) {
  159 |       // Wait for the last message to be visible
> 160 |       await this.messages.last().waitFor({state: 'visible', timeout: 20_000});
      |                                  ^
  161 |     }
  162 |
  163 |     // Then get all matching elements
    at ConversationPage.isMessageVisible (/home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/pageManager/webapp/pages/conversation.page.ts:160:34)
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/backupRestoration-TC-8634.spec.ts:87:33
    at /home/runner/actions-runner/_work/wire-webapp/wire-webapp/test/e2e_tests/specs/CriticalFlow/backupRestoration-TC-8634.spec.ts:72:3

Attempt 2
Result: ✅ Passed
Duration: 42170ms

Copy link
Contributor

@e-maad e-maad left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@thisisamir98 thisisamir98 merged commit 3b5c1ba into dev Oct 17, 2025
19 of 20 checks passed
@thisisamir98 thisisamir98 deleted the WPB-21112 branch October 17, 2025 11:26
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.

5 participants