Troubleshooting
If your scan isn’t working, the issue is almost always one of: install/auth, camera, lighting/face/motion conditions, or network. This page walks through each.
Install & Authentication
Section titled “Install & Authentication”npm install @circadify/web-sdk returns 401 / 404
Section titled “npm install @circadify/web-sdk returns 401 / 404”@circadify/web-sdk is a private package on GitHub Packages. You need a Personal Access Token with read:packages scope and a .npmrc that points the @circadify scope at the GitHub Packages registry. See Installation for the full walk-through.
@circadify:registry=https://npm.pkg.github.com//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}A 404 usually means your token doesn’t have access to the repo yet — contact support@circadify.com to be granted access.
MISSING_API_KEY thrown immediately
Section titled “MISSING_API_KEY thrown immediately”You called new CircadifySDK({ apiKey: '' }) or apiKey: undefined. Double-check your environment variable is loaded (process.env.NEXT_PUBLIC_CIRCADIFY_KEY, import.meta.env.VITE_CIRCADIFY_KEY, etc.).
INVALID_API_KEY after measureVitals()
Section titled “INVALID_API_KEY after measureVitals()”The key is wrong, revoked, or expired. Re-issue a new ck_live_* key from the Keys page in the developer portal and update your config.
Camera Issues
Section titled “Camera Issues”CAMERA_PERMISSION_DENIED
Section titled “CAMERA_PERMISSION_DENIED”The user blocked camera access — or permission state was inherited from a prior visit:
- In-tab: click the camera icon in the address bar and re-allow.
- macOS: System Settings → Privacy & Security → Camera → enable for this browser.
- iOS Safari: Settings → Safari → Camera → Ask or Allow.
- Cross-origin iframes: add
allow="camera"to the<iframe>.
CAMERA_NOT_AVAILABLE
Section titled “CAMERA_NOT_AVAILABLE”No camera was enumerated. Common causes:
- An external monitor with the lid closed disables the built-in webcam (open the lid or plug in a USB camera).
- A virtual webcam (Snap Camera, OBS) was uninstalled but is still selected as default — quit the helper app or reset device defaults.
- The browser is launched in an environment without media devices (headless Chromium, some CI runners).
CAMERA_IN_USE
Section titled “CAMERA_IN_USE”Another app already holds the camera. Quit Zoom, FaceTime, Photo Booth, or any other browser tabs that may be using it.
NotFoundError on macOS Chrome even when camera works elsewhere
Section titled “NotFoundError on macOS Chrome even when camera works elsewhere”Chrome’s Web Media constraints occasionally reject the SDK’s strict frameRate: { max: 30 } ceiling on certain macOS configurations. If you see NotFoundError and the camera works in other apps:
- Switch to
@circadify/react— it acquires the camera with permissive constraints (frameRate: { ideal: 30 }) and binds the stream to the SDK viavideoElement. - Or, in vanilla JS, call
navigator.mediaDevices.getUserMedia(...)yourself, attach the stream to your<video>, and passvideoElementtomeasureVitals()— the SDK will use the existing stream.
Scan Quality Issues
Section titled “Scan Quality Issues”FACE_DETECTION_TIMEOUT
Section titled “FACE_DETECTION_TIMEOUT”No face was detected for 30 seconds. Common causes:
- Face is partially out of frame — center it and stay 30–50 cm from the camera.
- Strong backlighting silhouettes the face — face a window or light source instead.
- Heavy face covering (mask, large hat brim, sunglasses).
- Camera is pointing at a wall or the subject’s chest, not their face.
The onLandmarks callback fires with empty arrays during this period — you can use that signal to render a “Position your face in frame” prompt.
QUALITY_TOO_LOW
Section titled “QUALITY_TOO_LOW”The capture finished but the signal-to-noise ratio was too poor for reliable extraction. Consume onQualityState to give live guidance during readiness:
lighting.isOk === false→ “Move to better lighting” (or “Reduce glare” iflighting.isTooBright)motion.isStill === false→ “Hold still”pose.isFacingForward === false→ “Face the camera directly”
A confidence of 0.0 in the result means fallback values were used — treat as “scan failed”, not as a real measurement.
Confidence score is consistently low
Section titled “Confidence score is consistently low”- Lighting is the #1 factor. Soft, diffuse front lighting (a window during day, or a lamp aimed at the face) produces the highest confidence.
- Skin tone calibration: passing
demographics.fitzpatrickimproves accuracy. - Camera quality matters less than lighting — a 720p webcam with good light beats a 4K webcam in shadow.
WASM / Module Loading
Section titled “WASM / Module Loading”WASM_LOAD_FAILED
Section titled “WASM_LOAD_FAILED”The MediaPipe vision engine or geometry engine could not be loaded. Causes:
- Network: the default CDN (
jsdelivr) is blocked on the user’s network. Self-host the WASM files and passwasmConfig: { visionWasmUrl, visionModelUrl, geometryEngineUrl }. Contact support for the distribution package. - CSP: a strict Content-Security-Policy blocks the WASM/script. Allow
wasm-unsafe-evaland the CDN origin inscript-src/connect-src. If you self-host, allow your own origin. - CORS: the model file is served without
Access-Control-Allow-Origin: *. Self-host with the right headers if your CDN strips them.
Slow first scan
Section titled “Slow first scan”The vision engine (~12 MB) loads on the first measureVitals() call and is cached by the browser thereafter. Subsequent scans skip the download. To prewarm, instantiate the SDK during page load — this kicks off WASM loading before the user clicks “Start”.
React-specific
Section titled “React-specific”<CircadifyProvider> re-creates the SDK on every render
Section titled “<CircadifyProvider> re-creates the SDK on every render”The SDK is memoized against apiKey, environment, and onDeviceOnly. If those values aren’t stable, the provider tears down and re-creates the SDK. Hoist the values into module scope, useState, or environment variables — don’t compute them inline on every render.
useSession().start() returns immediately, never resolves
Section titled “useSession().start() returns immediately, never resolves”You probably called it without a video element mounted yet. Pass videoElement: videoRef.current after the ref is bound — typically inside an effect or click handler, not during render.
Server-side rendering (Next.js, Remix)
Section titled “Server-side rendering (Next.js, Remix)”Both @circadify/web-sdk and @circadify/react are client-only — they touch navigator.mediaDevices. Wrap your scan UI in a dynamic import with ssr: false, or in a component marked 'use client' (Next.js App Router automatically gets the right directives because @circadify/react@1.x injects "use client" into its build output).
Network / Backend
Section titled “Network / Backend”NETWORK_ERROR or UPLOAD_FAILED
Section titled “NETWORK_ERROR or UPLOAD_FAILED”- Check
https://status.circadify.comfor incidents. - Verify outbound HTTPS to
api.circadify.comand the S3 upload URL returned by the session-start response is not blocked by a corporate firewall. - Both errors set
error.isRetryable === true— surface a retry button in your UI.
RATE_LIMITED
Section titled “RATE_LIMITED”Too many scans in a short window for the API key. Back off using the Retry-After header (or error.details.retryAfterSeconds). Contact your account admin to raise a sustained limit.
Still stuck?
Section titled “Still stuck?”Capture the output of sdk.getDeviceCapabilities(), the failing error.code, the value of error.cause if present, and your browser/OS version, then email support@circadify.com.