Skip to content

Picture-in-Picture fails with "NotAllowedError: Must be handling a user gesture" due to async event handling Description #1163

@kentcdodds

Description

@kentcdodds

I got an error in my Sentry logs and Seer suggested it's an issue with media-chrome. Here's the issue it generated. Hopefully this is enough context:

The Mux Player's Picture-in-Picture feature fails when triggered by user clicks because the internal event handling introduces an asynchronous delay that breaks the browser's user gesture context requirement.

Error

NotAllowedError: Failed to execute 'requestPictureInPicture' on 'HTMLVideoElement': Must be handling a user gesture if there isn't already an element in Picture-in-Picture.

Root Cause Analysis

Based on the stack trace, the issue occurs in this sequence:

  1. User clicks on the video player (valid user gesture)
  2. HTMLElement.handleClick processes the initial click
  3. Internal mediaenterpiprequest event is triggered
  4. Event goes through Object.dispatch mechanism (introduces async delay)
  5. Finally requestPictureInPicture is called, but browser no longer considers it a direct user gesture
  6. The critical issue is in step 4: the internal event dispatching mechanism breaks the synchronous link between the user's click and the requestPictureInPicture call.

Browser Requirements

According to the HTML specification, requestPictureInPicture() must be called synchronously within a user gesture event handler. Any asynchronous operations (promises, timeouts, event dispatching) will cause the browser to reject the call with a NotAllowedError.

Stack Trace

[as requestPictureInPicture] in file /assets/epic-video-hJ3IuML-.js [Line 115, column 608] (In app)
Object.set in file /assets/epic-video-hJ3IuML-.js [Line 303, column 20152] (In app)
Object.mediaenterpiprequest in file /assets/epic-video-hJ3IuML-.js [Line 303, column 27938] (In app)
Object.dispatch in file /assets/epic-video-hJ3IuML-.js [Line 303, column 32124] (In app)
HTMLElement.<anonymous> in file /assets/epic-video-hJ3IuML-.js [Line 303, column 33888] (In app)
HTMLElement.handleClick in file /assets/epic-video-hJ3IuML-.js [Line 1113, column 735] (In app)

Expected Behavior

Picture-in-Picture should work when users click the PiP button, similar to how it works in native browser video controls.

Suggested Fix

The PiP request should be made synchronously within the click event handler, without going through internal event dispatching mechanisms. For example:

// Instead of dispatching events, call PiP directly in the click handler
handlePipClick(event) {
  event.preventDefault();
  if (this.mediaEl && document.pictureInPictureEnabled) {
    if (document.pictureInPictureElement) {
      document.exitPictureInPicture();
    } else {
      this.mediaEl.requestPictureInPicture();
    }
  }
}

Environment

Mux Player React version: @mux/mux-player-react@3.5.1
Browser: Chrome/Edge (Chromium-based browsers)
Framework: React with React Router

Workaround

Currently implementing a custom PiP button that bypasses Mux Player's internal handling:

const handlePipClick = () => {
  const video = muxPlayerRef.current?.mediaEl
  if (video && document.pictureInPictureEnabled) {
    if (document.pictureInPictureElement) {
      document.exitPictureInPicture()
    } else {
      video.requestPictureInPicture()
    }
  }
}

This workaround requires hiding the default PiP button and implementing a custom one, which is not ideal for maintaining consistency with the player's UI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions