Skip to content

fix: <vimeo-video> element race condition#220

Open
spuppo-mux wants to merge 4 commits intomuxinc:mainfrom
spuppo-mux:fix/vimeo-video-element-race-condition
Open

fix: <vimeo-video> element race condition#220
spuppo-mux wants to merge 4 commits intomuxinc:mainfrom
spuppo-mux:fix/vimeo-video-element-race-condition

Conversation

@spuppo-mux
Copy link
Copy Markdown
Contributor

@spuppo-mux spuppo-mux commented Mar 23, 2026

By checking logs I saw that when the bug happened, we were getting into the isInit === false case (and load was running just one time), so the options weren't being applied.

Tested this mainly on nextjs example. Could originally reproduce semi-consistently by switching to TikTok player and then back to vimeo. When the bug happens we see the Vimeo default color (blueish)
Screenshot 2026-03-20 at 5 30 19 PM
When the config applies correctly we should see a pinkish color
Screenshot 2026-03-20 at 5 29 43 PM

To address the race condition:

  • Moved await (this.#loadRequested = Promise.resolve()); to the top of the function
  • Added a disconnectedCallback to clear state on unmount.

Extra:

  • Code clearness:
    • Moved api listener setup into a new #setupApiListeners function
    • Moved #onLoaded listener to a new method since it's now used from two functions
    • Restrctured "if else" at the bottom of load function so it's clearer to read.
  • Open points (non-blocking):
    • Noticed that isInit and hasLoaded are pretty similar semantically, was wondering if one of them can be removed.
    • Added options to new VimeoPlayerAPI call (did not seem to have effect even though it's specified in the JSdocs)
/**
   * Create a Player.
   *
   * @param {(HTMLIFrameElement|HTMLElement|string|jQuery)} element A reference to the Vimeo
   *        player iframe, and id, or a jQuery object.
   * @param {object} [options] oEmbed parameters to use when creating an embed in the element.
   * @return {Player}
   */

Note

Medium Risk
Changes the vimeo-video-element load sequencing and lifecycle state reset, which can affect initialization timing and event ordering across mounts/unmounts.

Overview
Fixes a race in VimeoVideoElement.load() by moving the one-tick #loadRequested gate earlier, so attribute/config updates settle before initialization and options are reliably applied.

Refactors load completion handling by extracting #onLoaded() and listener wiring into #setupApiListeners(), passing options into new VimeoPlayerAPI(...), and adding disconnectedCallback() to clear internal init/load flags and reset loadComplete when the element unmounts.

Written by Cursor Bugbot for commit 359a7fc. This will update automatically on new commits. Configure here.

@snyk-io
Copy link
Copy Markdown

snyk-io bot commented Mar 23, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 23, 2026

@spuppo-mux is attempting to deploy a commit to the Mux Team on Vercel.

A member of the Team first needs to authorize it.

@spuppo-mux
Copy link
Copy Markdown
Contributor Author

Closes #219

@spuppo-mux spuppo-mux linked an issue Mar 23, 2026 that may be closed by this pull request
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

this.#loadRequested = null;

if (this.#hasLoaded) this.loadComplete = new PublicPromise();
this.#hasLoaded = true; // TODO: Identify how hasLoaded differs from isInit
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reordered loadComplete refresh enables null api dereference

Medium Severity

Moving if (this.#hasLoaded) this.loadComplete = new PublicPromise() from before the 1-tick await to after it introduces a timing regression on second+ loads. When src and loop change in the same synchronous block (common in React/Next.js), the loop handler's await this.loadComplete resolves immediately on the stale promise. Its continuation then runs after load() sets this.api = null, causing this.api.setLoop(...) to throw a TypeError. Previously, loadComplete was refreshed before the await, so the loop handler would correctly suspend until the new load completed.

Additional Locations (1)
Fix in Cursor Fix in Web

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.

<vimeo-video> config not being set

1 participant