NotAllowedError: Permission denied (getUserMedia)
The page asked for camera or microphone and the browser refused before any media flowed. No tracks, no preview — often a permission, secure-context, or Permissions-Policy problem.
Typical messages
DOMException: Permission denied
NotAllowedError: Permission denied by system
NotAllowedError: The request is not allowed by the user agent or the platform
// Chrome site settings: Camera → Block
// Safari: Preferences → Websites → Camera
Related names: NotFoundError (no device), NotReadableError (device in use / OS busy), OverconstrainedError (constraints too tight), SecurityError (insecure context).
Checklist
- Secure context —
navigator.mediaDevicesrequireshttps://orhttp://localhost/127.0.0.1. Plainhttp://192.168.…on a phone will deny. Use TLS or a tunnel for device tests. - User gesture — call
getUserMediafrom a click/tap handler the first time. Auto-calling on page load is blocked or ignored in modern browsers. - Previously blocked — the user (or you while testing) chose Block. Reset via the padlock / site settings; browsers do not show a prompt again until reset.
- Permissions-Policy / Feature-Policy — response header or iframe attribute may disable camera/microphone:
Cross-origin embeds need both the policy and explicitPermissions-Policy: camera=(), microphone=() # blocks everything # iframe embed needs: <iframe allow="camera; microphone; display-capture" src="…">allow. - OS-level privacy — macOS/Windows/iOS can deny the whole browser. Check System Settings → Privacy → Camera / Microphone for Chrome/Firefox/Safari/Edge.
- In-use device — another app holds an exclusive lock → often
NotReadableError, not NotAllowedError. Close Zoom/Teams/OBS and retry. - Constraints —
exactwidth/facingMode that no camera meets →OverconstrainedError. Loosen constraints; enumerate devices after permission withenumerateDevices.
Minimal pattern
btn.onclick = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: { facingMode: "user" },
});
video.srcObject = stream;
} catch (err) {
if (err.name === "NotAllowedError") {
// show how to re-enable in site settings
} else if (err.name === "NotFoundError") {
// no camera/mic attached
} else {
console.error(err.name, err.message);
}
}
};
After permission succeeds
- Attach tracks to an
RTCPeerConnectionfor calls; networking failures then show up as ICE failed. - Thin upload pipes still make you look blurry to others — see why video calls go blurry.
- Stop tracks with
track.stop()when leaving the page so the OS privacy light turns off.
Not the same as
- WebRTC ICE failed — permission worked; network path did not.
- WebSocket connection failed — signalling channel, not camera.
- connect-src CSP — fetch/WebSocket targets blocked by Content-Security-Policy (different API).
Related
WebRTC ICE failed · WebSocket connection failed · Mixed Content blocked · frame-ancestors / iframe · Wirebook: video-call compression
Questions
What does NotAllowedError Permission denied mean for getUserMedia?
The browser refused camera or microphone access. Check site permissions, HTTPS/localhost, user gesture, OS privacy toggles, and Permissions-Policy / iframe allow.
Why is there no prompt?
The site was blocked earlier, the call was not from a user gesture, or a Permissions-Policy disables the feature. Reset site settings and call from a button click.
Works on desktop, fails on phone over LAN IP
Phones treat http://192.168.x.x as insecure. Use https (or USB debugging / a tunnel) so the context is secure.
Is Permission denied the same as ICE failed?
No. NotAllowedError is device permission before any PeerConnection network work. ICE failed is connectivity after tracks exist.
getDisplayMedia also throws NotAllowedError
Screen share needs its own permission and usually a fresh user gesture; some browsers disallow it in iframes without display-capture.
How do I detect a permanent block?
After a denial, permissions.query({name:'camera'}) may be 'denied'. Guide the user to the padlock site settings — you cannot re-prompt until they reset.
Published .