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

  1. Secure context — navigator.mediaDevices requires https:// or http://localhost / 127.0.0.1. Plain http://192.168.… on a phone will deny. Use TLS or a tunnel for device tests.
  2. User gesture — call getUserMedia from a click/tap handler the first time. Auto-calling on page load is blocked or ignored in modern browsers.
  3. 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.
  4. Permissions-Policy / Feature-Policy — response header or iframe attribute may disable camera/microphone:
    Permissions-Policy: camera=(), microphone=()   # blocks everything
    # iframe embed needs:
    <iframe allow="camera; microphone; display-capture" src="…">
    Cross-origin embeds need both the policy and explicit allow.
  5. OS-level privacy — macOS/Windows/iOS can deny the whole browser. Check System Settings → Privacy → Camera / Microphone for Chrome/Firefox/Safari/Edge.
  6. In-use device — another app holds an exclusive lock → often NotReadableError, not NotAllowedError. Close Zoom/Teams/OBS and retry.
  7. Constraints — exact width/facingMode that no camera meets → OverconstrainedError. Loosen constraints; enumerate devices after permission with enumerateDevices.

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

Not the same as

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 .

Try it

Related guides

Newest guides