← Debugging Guides

阅读中文版本

NotAllowedError — Autoplay Blocked by Browser Policy

Handle NotAllowedError from play() by requiring a user gesture, muting initially, and using a fallback play control.

beginnerdebuggingbrowsermediaautoplay

Symptoms

  • Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first
  • A media element does not start on page load
  • Audio works after a click but not automatically

Possible causes

  • The browser requires a user gesture before unmuted playback
  • Autoplay policy blocks media with sound on the origin
  • play() is called before the user has interacted

Diagnosis steps

Reproduce and read the rejection

document.querySelector('video').play().catch(e => console.log(e.name, e.message))

Interpretation

NotAllowedError with the interaction message confirms the autoplay policy, not a source error.

Try muted playback

video.muted = true; video.play().catch(console.error)

Interpretation

Muted autoplay is widely allowed, so starting muted distinguishes policy from media errors.

Root cause

Browsers block audible autoplay to protect users. play() returns a rejected promise with NotAllowedError until the user has interacted with the document or the media is muted.

Fix

Start muted and allow the user to unmute, call play() from within a click/keypress handler, and always catch the play() promise to surface a play control instead of an uncaught rejection.

Risk note

Repeatedly retrying play() without a gesture keeps rejecting and can spam logs; react to the gesture instead.

Verify the fix

Load the page and confirm muted playback starts; click to unmute and confirm audio plays without an uncaught error.

Symptoms

This debugging guide addresses “NotAllowedError — Autoplay Blocked by Browser Policy”. Start from the symptoms, follow the ordered diagnostics, and confirm each hypothesis before moving on; the fix and verification steps then close the loop.

Observed symptoms include:

  • Uncaught (in promise) DOMException: play() failed because the user didn’t interact with the document first
  • A media element does not start on page load
  • Audio works after a click but not automatically

Reproduce and Diagnose

Reproduce the failure in isolation before changing any code. Work through the checks below in order; each one narrows the search space.

Step 1: Reproduce and read the rejection

document.querySelector('video').play().catch(e => console.log(e.name, e.message))

NotAllowedError with the interaction message confirms the autoplay policy, not a source error.

Step 2: Try muted playback

video.muted = true; video.play().catch(console.error)

Muted autoplay is widely allowed, so starting muted distinguishes policy from media errors.

Likely contributors, in order of frequency:

  • The browser requires a user gesture before unmuted playback
  • Autoplay policy blocks media with sound on the origin
  • play() is called before the user has interacted

Root Cause

Browsers block audible autoplay to protect users. play() returns a rejected promise with NotAllowedError until the user has interacted with the document or the media is muted.

The Fix

Start muted and allow the user to unmute, call play() from within a click/keypress handler, and always catch the play() promise to surface a play control instead of an uncaught rejection.

Watch out: Repeatedly retrying play() without a gesture keeps rejecting and can spam logs; react to the gesture instead.

const video = document.querySelector('video');
video.muted = true; // allowed to autoplay when muted
try {
  await video.play();
} catch (err) {
  if (err.name === 'NotAllowedError') {
    showPlayButton(); // require a user gesture
  } else {
    console.error(err);
  }
}

// or start from a gesture
playButton.addEventListener('click', () => {
  video.muted = false;
  video.play().catch(console.error);
});

Verify and Prevent

Load the page and confirm muted playback starts; click to unmute and confirm audio plays without an uncaught error.

Once verified, add a regression test that reproduces the original symptom so the failure cannot silently return.

This class of failure is usually environmental rather than pure logic, so capture the exact command, the environment variables, and the working directory in your bug report. Reproduce it in the smallest possible script, then widen the scope only once the minimal case is understood. A short written checklist for this failure mode saves hours the next time it appears.