Navigator getusermedia is not a function
Why navigator.getUserMedia is undefined or not a function, how to switch to navigator.mediaDevices.getUserMedia, and how to diagnose the errors that follow.
The error "navigator.getUserMedia is not a function" usually means your code calls an old form of the browser's camera and microphone API, and the current browser no longer provides it. In most cases, you fix it by calling navigator.mediaDevices.getUserMedia instead and making sure the page runs in a secure context. This is a code and browser-environment problem, not a hardware fault. A webcam that works in other apps does not prevent this error, and the error does not mean your webcam or microphone is broken.
The short answer
Replace navigator.getUserMedia(constraints, onSuccess, onError) with navigator.mediaDevices.getUserMedia(constraints). The new method returns a Promise, so handle the result with await or .then() and .catch() instead of passing callbacks. If the error then changes to "Cannot read properties of undefined (reading 'getUserMedia')", navigator.mediaDevices itself is missing. The most common cause is that the page is not served over HTTPS or from localhost.
Why the old method is missing
Early versions of camera and microphone capture were attached directly to the navigator object. They used a callback style, and browsers often shipped them under vendor-prefixed names such as webkitGetUserMedia and mozGetUserMedia. The standardized version moved under navigator.mediaDevices and returns a Promise. The old navigator.getUserMedia is deprecated, so a given browser may expose it, expose only a prefixed version, or not provide it at all. Older tutorials, code samples and unmaintained libraries still use the legacy call, which is why the error keeps appearing in new projects.
Gap: no confirmed data was supplied about which browser versions removed or still support the legacy method. Check current browser compatibility documentation for the browsers you need to support, rather than relying on version numbers from older articles.
Match the exact error message to its cause
| Error message | What it usually means | Next action |
| navigator.getUserMedia is not a function / navigator.getUserMedia is undefined | The legacy method is not provided in this browser | Switch to navigator.mediaDevices.getUserMedia |
| Cannot read properties of undefined (reading 'getUserMedia') | navigator.mediaDevices is undefined, typically because the page is not a secure context, or the browser or embedded view does not support it | Serve the page over HTTPS or localhost, then test in a full desktop or mobile browser |
| navigator is not defined | The code runs outside a browser, for example during server-side rendering, a build step or a Node.js script | Run the capture code only on the client, after the page has loaded |
| No error, but success or error callbacks never run | Callbacks were passed to mediaDevices.getUserMedia, which ignores them and returns a Promise | Use await or .then() and .catch() on the returned Promise |
Checks that narrow it down
- 1. Open the developer console on the affected page and run: typeof navigator.mediaDevices. If it prints "undefined", the browser is not exposing the modern API on this page. Continue with the secure-context check.
- 2. Run: window.isSecureContext. If it prints false, load the page over HTTPS, or over localhost during development. Plain HTTP on a network IP address or a custom local hostname is generally not treated as secure.
- 3. Check where the code runs. In frameworks that render on the server, move the capture call into client-only code, such as an effect or event handler that runs after mount.
- 4. Check the browser. Some in-app browsers and embedded web views do not expose camera capture. Open the same page in a full browser and retest.
- 5. Check for an iframe. If the page is embedded in another page, the iframe needs an allow attribute that includes camera and microphone. Without it, the call usually fails with a permission error rather than "not a function", but this is worth checking once the API is present.
- 6. Search your code and dependencies for getUserMedia, webkitGetUserMedia and mozGetUserMedia. An outdated library can trigger the error even if your own code is correct. Update the library or replace it.
Modern code with clear failure messages
async function startCamera() {
if (!window.isSecureContext) {
throw new Error('Camera access requires HTTPS or localhost');
}
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
throw new Error('mediaDevices.getUserMedia is not available in this browser');
}
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
document.querySelector('video').srcObject = stream;
return stream;
}
Checking in this order separates the three different problems: an insecure page, a browser without the API, and a capture request that started but failed.
Optional fallback for older browsers
If you must support browsers that only have the legacy or prefixed method, wrap it in a Promise so the rest of your code uses one style. A fallback does not make an insecure page work, and it does not help in browsers that provide neither version. The WebRTC adapter shim is another option if you want broader normalization across browsers.
function getMedia(constraints) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
return navigator.mediaDevices.getUserMedia(constraints);
}
const legacy = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
if (!legacy) {
return Promise.reject(new Error('getUserMedia is not supported here'));
}
return new Promise((resolve, reject) => {
legacy.call(navigator, constraints, resolve, reject);
});
}
Once the function exists: constraint and permission errors
After the call is fixed, any remaining failures arrive as a rejected Promise with a named error. Log err.name and err.message, then use the table below to decide whether the issue is in your constraints, the browser's permissions, the operating system or the device.
| Error name | Likely cause | What to check next |
| NotAllowedError | The user or browser denied permission, the page is in an iframe without camera or microphone allowed, or the operating system blocks the browser | Site permissions in the browser, the iframe allow attribute, and the operating system's camera and microphone privacy settings for the browser |
| NotFoundError | No device matches the request, for example no camera is connected or recognized | Whether the operating system lists the device, and whether you requested video or audio that does not exist |
| NotReadableError | The device exists but could not be opened, often because another app is using it or a driver problem | Close other apps using the camera or microphone, reconnect the device and retest |
| OverconstrainedError | A constraint, such as an exact resolution, frame rate or deviceId, cannot be met | Read err.constraint to see which property failed, and use ideal values instead of exact ones |
| TypeError | The constraints object is empty, or both audio and video are false | Request at least one of audio or video |
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: { ideal: 1280 }, height: { ideal: 720 } }
});
} catch (err) {
if (err.name === 'OverconstrainedError') {
console.log('Constraint that could not be met:', err.constraint);
} else {
console.log(err.name, err.message);
}
}
If you select a specific device by deviceId, get the current IDs from navigator.mediaDevices.enumerateDevices() instead of hard-coding them. Device IDs can change and are only fully available after permission is granted. Prefer deviceId: { ideal: id } unless the user has explicitly chosen a device and failing is the correct outcome.
Separating code, browser, operating system and hardware
"Not a function" and "undefined" errors happen before any permission prompt or device access. Changing camera settings or reconnecting hardware will not fix them. Only once getUserMedia actually runs do the other layers matter, in this order:
1) Browser site permissions for the page's origin.
2) Operating-system privacy settings that allow or block the browser's camera and microphone access.
3) Other apps holding the device.
4) The physical connection.
After each change, reload the page and retest. A permission change often takes effect only after a reload.
Compatibility, permission and privacy limits
- Support varies by browser and context. Test every browser you target, including mobile browsers and any in-app browsers your users are likely to open the page in.
- Permission is granted per origin. Users can revoke it at any time, and browsers differ in whether they remember the choice or ask again. Handle NotAllowedError gracefully instead of assuming access persists.
- Stop the stream when you are done with stream.getTracks().forEach(track => track.stop()). This releases the device and turns off the browser's camera indicator.
- A working local preview shows that capture works on that device and browser. It does not test call quality, network performance, or how a service processes, transmits, stores or retains the media. Verify those practices in the service's own documentation, because a permission prompt does not describe them.
- Camera frames can show the room behind the user. If your page records or uploads video, tell users before capture starts so they can adjust their surroundings or background.