NotAllowedError — Autoplay Blocked by Browser Policy
Handle NotAllowedError from play() by requiring a user gesture, muting initially, and using a fallback play control.
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.