MY WEBCAM TEST GUIDE

Why navigator.mediaDevices.getUserMedia Isn't Working in Your Browser

getUserMedia failing in Chrome or another browser? Learn the main causes, including an insecure page, blocked permissions, a busy device or impossible constraints, and the order to check them.

The short answer

When getUserMedia fails, the cause almost always falls into one of four groups: the page is not a secure context, so the API is missing; access was denied by the user, the browser or the operating system; no usable camera or microphone is available; or the constraints you passed cannot be satisfied. The error name returned by the rejected promise tells you which group you are in. One condition changes the diagnosis completely: if navigator.mediaDevices is undefined, the problem is not permissions or hardware. The page is not running in a secure context, or the browser does not expose the API at all.

  • Secure context: the API is only available on HTTPS pages and on localhost during development.
  • Permission: camera and microphone access can be blocked separately, at the site level in the browser or at the app level in the operating system.
  • Device availability: the requested kind of device may be missing, disconnected or already in use by another program.
  • Constraints: required values such as exact, min or max can rule out every device that is actually present.

First, identify the type of failure

There are two quite different symptoms. In the first, your code throws before any request is made, typically a TypeError saying it cannot read getUserMedia of undefined. This means navigator.mediaDevices does not exist on the page. In the second, the call runs but the returned promise rejects with a named error, which is a DOMException in most cases. Logging the error name, its message and, for constraint failures, its constraint property is the quickest way to tell these apart.

if (!window.isSecureContext) {
  console.warn('Not a secure context: getUserMedia is unavailable.');
} else if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
  console.warn('getUserMedia is not supported in this browser.');
} else {
  navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then((stream) => {
      console.log(stream.getTracks().map((t) => `${t.kind}: ${t.label}`));
    })
    .catch((err) => {
      console.error(err.name, err.message, err.constraint || '');
    });
}

If you request audio and video together, a problem with either device can fail the whole call. Testing { video: true } and { audio: true } on their own shows whether the camera, the microphone or both are responsible.

What each error name means

Error | What it usually means | What to check
TypeError (mediaDevices is undefined) | Page is not a secure context, or the browser lacks the API | Serve the page over HTTPS or from localhost; check window.isSecureContext
TypeError (from the call itself) | The constraints object requests nothing, for example both audio and video are false or missing | Request at least one of audio or video
NotAllowedError | Access was denied by the user, by a saved site setting, by an OS privacy setting, or because an embedded frame lacks permission | Site permissions, OS camera and microphone privacy settings, iframe allow attribute
NotFoundError | No device of the requested kind matches the request | Whether a camera or microphone is connected and recognized
NotReadableError | The device exists, but the browser could not open it, often because another app or tab is using it | Close other apps or tabs using the device; reconnect it
OverconstrainedError | No device can meet the required constraints; err.constraint names the one that failed | Relax exact, min and max values, or drop a stale deviceId
SecurityError | Media access is disabled for this document | Secure context and page or frame permission policy
AbortError | Something other than the cases above prevented the device from being used | Retry, then check the device and other software using it

The order of checks

  • 1. Confirm the page is a secure context, using HTTPS or localhost.
  • 2. If your code runs inside an iframe, confirm the frame is allowed to use the camera and microphone.
  • 3. Check the browser's permission for this site, separately for the camera and the microphone.
  • 4. Check the operating system's privacy settings to make sure the browser itself is allowed to use the camera and microphone.
  • 5. Confirm the device is detected by the system and appears in enumerateDevices().
  • 6. Make sure no other application or tab is holding the device.
  • 7. Simplify your constraints to { video: true } or { audio: true }, then add requirements back one at a time.

Secure context: the most common reason the API is missing

Browsers only provide getUserMedia on secure origins. In practice, this means pages served over HTTPS. Pages on localhost are also generally treated as secure, which is why code can work on a development machine. The same code often fails when you open the page over plain HTTP using a local network IP address, for example to test on a phone. On such a page, navigator.mediaDevices is simply undefined. Checking window.isSecureContext confirms this immediately. The fix is to serve the page over HTTPS, including during local testing on other devices.

Permissions: browser, operating system and embedded frames

Camera permission and microphone permission are separate. Granting one does not grant the other. In Chrome, the site's current permission is shown in the site controls next to the address bar and in the browser's site settings. If a user previously chose to block access, Chrome remembers that choice and rejects later requests without prompting again until the setting is changed.

The operating system adds a second layer. macOS has per-app camera and microphone access under Privacy & Security, and Windows has camera and microphone privacy settings that control which apps can use them. If the OS blocks the browser, the site can be fully allowed and the request will still fail. Which error name appears in that case depends on the browser and platform, so do not rule out an OS block just because you see something other than NotAllowedError.

If your capture code runs inside an iframe, especially one from a different origin, the embedding page must grant the frame permission to use these devices through the allow attribute. Without it, the request is denied regardless of the user's settings.

<iframe allow="camera; microphone"></iframe>

Detection, access and selection are different steps

Seeing a device does not mean you can use it, and getting access does not mean you have the input you intended. enumerateDevices() reports which cameras and microphones exist, but labels are usually blank until the user has granted permission. Device IDs are tied to the site and can change, for example after the user clears site data. If your code saves a deviceId and later requests it with exact, a changed or missing ID causes an OverconstrainedError even though a working camera is connected. If a request succeeds but the wrong camera or microphone is active, the cause is input selection rather than a failure. Check the track label and choose the device explicitly.

Constraint errors: when the request is too specific

Constraints written with exact, min or max are requirements. Plain values and ideal are preferences that the browser tries to match where it can. If no available device can satisfy a required value, the promise rejects with OverconstrainedError, and its constraint property names the setting that could not be met. Typical causes are an exact resolution or frame rate that the camera does not support, a facingMode that the hardware lacks, or a stale deviceId. To fix this, state preferences with ideal and fall back to a simpler request when a strict one fails.

async function openCamera(savedId) {
  try {
    return await navigator.mediaDevices.getUserMedia({
      video: {
        deviceId: savedId ? { exact: savedId } : undefined,
        width: { ideal: 1280 },
        height: { ideal: 720 }
      }
    });
  } catch (err) {
    if (err.name === 'OverconstrainedError') {
      console.warn('Constraint could not be met:', err.constraint);
      return navigator.mediaDevices.getUserMedia({ video: true });
    }
    throw err;
  }
}

Notes specific to Chrome

  • A blocked camera or microphone permission is stored per site and must be reset before Chrome will prompt again.
  • Chrome uses the default camera and microphone chosen in its own settings unless your constraints request a specific device.
  • Another program or tab using the camera can cause NotReadableError. This is a common report on Windows, where a device is often used by one application at a time.
  • Browser extensions or managed browser policies can also restrict camera and microphone access, which makes the problem look like an ordinary permission denial.

Limits of this guide

This guide covers documented API behavior and general troubleshooting principles. It does not include test results, version-by-version compatibility data or measurements for particular browsers, operating systems or cameras. Exact error names and settings locations can vary between versions, so if a step does not match what you see, check the current documentation for your specific browser and operating system.