FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: keep first Jitsi call on its jwt navigation on cold cache (SUP-1072) by jeanfbrito · Pull Request #3416 · RocketChat/Rocket.Chat.Electron · GitHub

fix: keep first Jitsi call on its jwt navigation on cold cache (SUP-1072) - #3416

Merged
jeanfbrito merged 1 commit into
hotfix/4.15.5from
fix/video-call-cold-cache-recovery
Jul 15, 2026
Merged

fix: keep first Jitsi call on its jwt navigation on cold cache (SUP-1072)#3416
jeanfbrito merged 1 commit into
hotfix/4.15.5from
fix/video-call-cold-cache-recovery

Conversation

jeanfbrito commented Jul 15, 2026
edited by coderabbitai Bot
Loading

Copy link
Copy Markdown
Member

What

Fixes the cold-cache first-call flow in the video call window: the first Jitsi call after a fresh install/update (or cleared cache) no longer drops to the Jitsi prejoin page — it stays on the jwt-carrying navigation and auto-joins.

Jira: SUP-1072

Why

On a cold cache, the Jitsi page commits and consumes the jwt=...&prejoinPageEnabled=false URL while its bundles are still downloading, so did-finish-load can arrive after the 15s loading timeout. The auto-recovery path — designed for navigations that never load — did not cover this committed-but-still-downloading case: it reloaded the current URL, which Jitsi had already jwt-stripped via history.replaceState, aborting the in-flight load (did-fail-load with ERR_ABORTED -3) and finishing on the tokenless URL, i.e. the prejoin page. All recovery logging was dev-gated, so production logs showed no trace of the trigger.

How

All changes in src/videoCallWindow/video-call-window.ts:

  • Page commit disarms the loading timeout (dom-ready on the webview): once the page committed, Jitsi already consumed the jwt; the timeout now only covers navigations that never commit.
  • Recovery attempt 1 re-navigates to the original validated call URL (state.url, frozen at webview creation with the jwt intact) instead of blind reload() of the current — possibly tokenless — URL. Falls back to reload() only when no stored URL exists.
  • did-fail-load ignores ERR_ABORTED (-3) so self-inflicted navigation aborts don't trigger the error UI or further recovery.
  • Timeout/recovery logs are now unconditional console.warn so support logs capture recovery activity in production.
  • about:blank hop guard: recovery attempt 2's placeholder navigation no longer resets the bounded 3-attempt escalation ladder.

Validation

  • yarn lint, npx tsc --noEmit: clean
  • yarn test (videoCallWindow suites): 44/44 pass
  • yarn build: clean
  • Runtime cold-cache repro requires a throttled network + Jitsi with JWT; QA steps: clear app cache, throttle to ~1 Mbps, start a Jitsi call → expect auto-join with no prejoin page.

Notes

  • Base is hotfix/4.15.5 (created from the 4.15.4 tag) targeting a 4.15.5 hotfix release.
  • Follow-up candidate for the same hotfix: backport of fix: externalize dependency subpath imports in rollup bundles #3411 (rollup subpath externals) — the exports is not defined error in the customer logs comes from the bundled react-dom/client chunk in the screen-share picker path at 4.15.x.

Summary by CodeRabbit

  • Bug Fixes
    • Improved video call recovery during interrupted or failed navigation.
    • Prevented temporary recovery navigation states from incorrectly resetting recovery progress.
    • Avoided showing error states for expected, internally aborted navigations.
    • Added clearer warnings when loading timeouts or recovery limits are reached.

On a cold cache the Jitsi page commits and consumes the jwt while its
bundles are still downloading, so did-finish-load can miss the 15s
loading timeout. Auto-recovery then reloaded the current URL — already
jwt-stripped by Jitsi's history.replaceState — aborting the in-flight
load (ERR_ABORTED -3) and landing the user on the prejoin page.

- clear the loading timeout once the webview page commits (dom-ready);
  the timeout now only covers navigations that never commit
- recovery attempt 1 re-navigates to the original validated call URL
  instead of blind-reloading the current (possibly tokenless) URL
- ignore self-inflicted ERR_ABORTED (-3) in did-fail-load
- make timeout/recovery logs production-visible (console.warn); they
  were dev-gated, which is why customer logs showed no recovery trace
- guard attempt 2's about:blank hop from resetting the bounded
  3-attempt escalation ladder

coderabbitai Bot commented Jul 15, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Walkthrough

The video call window now validates recovery URLs, supports an about:blank transition during recovery attempt 2, preserves recovery counters across placeholder navigation, upgrades recovery logs to warnings, and ignores self-inflicted aborted loads.

Changes

Video call recovery

Layer / File(s) Summary
Recovery attempt flow
src/videoCallWindow/video-call-window.ts
Recovery attempts validate URLs, assign webview.src or reload for attempt 1, and use an about:blank placeholder hop for attempt 2.
Recovery lifecycle handling
src/videoCallWindow/video-call-window.ts
Timeout and DOM-ready handling preserve recovery state during the placeholder hop, while ERR_ABORTED loads are logged and ignored.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested labels: type: bug

🚥 Pre-merge checks | ✅ 5 ✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: preserving the first cold-cache Jitsi call on its JWT navigation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • SUP-1072: Request failed with status code 401

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.

coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/videoCallWindow/video-call-window.ts`:
- Around line 278-317: Update the attempt 2 branch in attemptAutoRecovery to
handle a missing state.url when a webview exists: add an else path that advances
recoveryAttempt and invokes attemptAutoRecovery, matching the URL-validation
catch behavior so the chain reaches the next recovery attempt.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info ⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62010f05-73aa-48ad-9bcf-b191cf930865

📥 Commits

Reviewing files that changed from the base of the PR and between 1c1d426 and 628878f.

📒 Files selected for processing (1)
  • src/videoCallWindow/video-call-window.ts
📜 Review details ⏰ Context from checks skipped due to timeout. (6)
  • GitHub Check: check (macos-latest)
  • GitHub Check: check (windows-latest)
  • GitHub Check: check (ubuntu-latest)
  • GitHub Check: build (ubuntu-latest, linux)
  • GitHub Check: build (macos-latest, mac)
  • GitHub Check: build (windows-latest, windows)
🧰 Additional context used 📓 Path-based instructions (1) **/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript for new code unless explicitly told otherwise.
Use Fuselage components from @rocket.chat/fuselage for UI work unless the design requires something Fuselage does not provide.
Check Theme.d.ts for valid color tokens before using Fuselage colors.
Verify library props, APIs, and tokens against official docs or local .d.ts files instead of assuming.
Use React functional components with hooks.
Redux actions follow FSA shape.
Use camelCase for file names and PascalCase for components.
Prefer clear names over unnecessary comments.
Prefer editing existing files over creating new abstractions unless the new abstraction removes real complexity or matches an existing pattern.

**/*.{ts,tsx}: Use TypeScript for all new code unless explicitly told otherwise.
Use Fuselage components for all UI work; create custom components only when Fuselage lacks the required functionality.
Import Fuselage components from @rocket.chat/fuselage.
Use only valid color tokens documented by Theme.d.ts.
Use optional chaining with fallbacks for platform-specific APIs, especially Linux-only process APIs such as process.getuid(), getgid(), geteuid(), and getegid().
Use TypeScript strict mode.
Redux actions must follow the Flux Standard Action pattern.
Use camelCase for file names and PascalCase for component names.
Avoid unnecessary comments; prefer self-documenting code through clear naming.
Do not commit or push without explicit user permission.
Verify library APIs, props, tokens, and types against official documentation and .d.ts files instead of assuming they are valid.

Files:

  • src/videoCallWindow/video-call-window.ts
🔇 Additional comments (1)
src/videoCallWindow/video-call-window.ts (1)

64-66: LGTM!

Also applies to: 241-243, 425-427, 441-456, 538-544

Comment on lines +278 to +317
console.warn(
`Video call window: Auto-recovery attempt ${currentAttempt}/${MAX_RECOVERY_ATTEMPTS} - ${strategy}`
);

recoveryTimeout = setTimeout(() => {
const webview = state.webview as any;

switch (currentAttempt) {
case 1:
if (webview) {
if (webview && state.url) {
try {
webview.src = validateVideoCallUrl(state.url);
} catch (error) {
console.error(
'Video call window: URL validation failed during recovery:',
error
);
console.error(
'Video call window: Skipping webview reload recovery step, proceeding to next recovery attempt'
);
if (recoveryTimeout) {
clearTimeout(recoveryTimeout);
recoveryTimeout = null;
}
state.recoveryAttempt = currentAttempt;
attemptAutoRecovery();
return;
}
} else if (webview) {
webview.reload();
}
break;
case 2:
if (webview && state.url) {
try {
const validatedUrl = validateVideoCallUrl(state.url);
isAboutBlankRecoveryHop = true;
webview.src = 'about:blank';
setTimeout(() => {
isAboutBlankRecoveryHop = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Ensure the recovery chain continues if the URL is missing during attempt 2.

If state.url is missing (which is possible per the upstream IPC contract), the if (webview && state.url) block evaluates to false and does nothing. This breaks the recovery chain entirely: no navigation occurs, did-start-loading never fires, and no further timeouts are scheduled, leaving the UI permanently stuck in a loading state without ever reaching attempt 3.

Add an else block to explicitly proceed to the next attempt, matching the behavior in the URL validation catch block.

🐛 Proposed fix
       case 2:
         if (webview && state.url) {
           try {
             const validatedUrl = validateVideoCallUrl(state.url);
             isAboutBlankRecoveryHop = true;
             webview.src = 'about:blank';
             setTimeout(() => {
               isAboutBlankRecoveryHop = false;
               if (webview) {
                 webview.src = validatedUrl;
               }
             }, 500);
           } catch (error) {
             console.error(
               'Video call window: URL validation failed during recovery:',
               error
             );
             console.error(
               'Video call window: Skipping URL refresh recovery step, proceeding to next recovery attempt'
             );
             if (recoveryTimeout) {
               clearTimeout(recoveryTimeout);
               recoveryTimeout = null;
             }
             state.recoveryAttempt = currentAttempt;
             attemptAutoRecovery();
             return;
           }
+        } else {
+          console.warn(
+            'Video call window: Missing URL during recovery attempt 2, proceeding to next recovery attempt'
+          );
+          state.recoveryAttempt = currentAttempt;
+          attemptAutoRecovery();
+          return;
         }
         break;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.warn(
`Video call window: Auto-recovery attempt ${currentAttempt}/${MAX_RECOVERY_ATTEMPTS} - ${strategy}`
);
recoveryTimeout = setTimeout(() => {
const webview = state.webview as any;
switch (currentAttempt) {
case 1:
if (webview) {
if (webview && state.url) {
try {
webview.src = validateVideoCallUrl(state.url);
} catch (error) {
console.error(
'Video call window: URL validation failed during recovery:',
error
);
console.error(
'Video call window: Skipping webview reload recovery step, proceeding to next recovery attempt'
);
if (recoveryTimeout) {
clearTimeout(recoveryTimeout);
recoveryTimeout = null;
}
state.recoveryAttempt = currentAttempt;
attemptAutoRecovery();
return;
}
} else if (webview) {
webview.reload();
}
break;
case 2:
if (webview && state.url) {
try {
const validatedUrl = validateVideoCallUrl(state.url);
isAboutBlankRecoveryHop = true;
webview.src = 'about:blank';
setTimeout(() => {
isAboutBlankRecoveryHop = false;
console.warn(
`Video call window: Auto-recovery attempt ${currentAttempt}/${MAX_RECOVERY_ATTEMPTS} - ${strategy}`
);
recoveryTimeout = setTimeout(() => {
const webview = state.webview as any;
switch (currentAttempt) {
case 1:
if (webview && state.url) {
try {
webview.src = validateVideoCallUrl(state.url);
} catch (error) {
console.error(
'Video call window: URL validation failed during recovery:',
error
);
console.error(
'Video call window: Skipping webview reload recovery step, proceeding to next recovery attempt'
);
if (recoveryTimeout) {
clearTimeout(recoveryTimeout);
recoveryTimeout = null;
}
state.recoveryAttempt = currentAttempt;
attemptAutoRecovery();
return;
}
} else if (webview) {
webview.reload();
}
break;
case 2:
if (webview && state.url) {
try {
const validatedUrl = validateVideoCallUrl(state.url);
isAboutBlankRecoveryHop = true;
webview.src = 'about:blank';
setTimeout(() => {
isAboutBlankRecoveryHop = false;
if (webview) {
webview.src = validatedUrl;
}
}, 500);
} catch (error) {
console.error(
'Video call window: URL validation failed during recovery:',
error
);
console.error(
'Video call window: Skipping URL refresh recovery step, proceeding to next recovery attempt'
);
if (recoveryTimeout) {
clearTimeout(recoveryTimeout);
recoveryTimeout = null;
}
state.recoveryAttempt = currentAttempt;
attemptAutoRecovery();
return;
}
} else {
console.warn(
'Video call window: Missing URL during recovery attempt 2, proceeding to next recovery attempt'
);
state.recoveryAttempt = currentAttempt;
attemptAutoRecovery();
return;
}
break;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/videoCallWindow/video-call-window.ts` around lines 278 - 317, Update the
attempt 2 branch in attemptAutoRecovery to handle a missing state.url when a
webview exists: add an else path that advances recoveryAttempt and invokes
attemptAutoRecovery, matching the URL-validation catch behavior so the chain
reaches the next recovery attempt.

Copy link
Copy Markdown

Copy link
Copy Markdown

macOS installer download

jeanfbrito merged commit 47d36de into hotfix/4.15.5 Jul 15, 2026
8 checks passed
jeanfbrito deleted the fix/video-call-cold-cache-recovery branch July 15, 2026 21:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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.

1 participant


Back | FazBrowse Home | New Git URL