Skip to content

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.

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.

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.).

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.

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>.

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).

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 via videoElement.
  • Or, in vanilla JS, call navigator.mediaDevices.getUserMedia(...) yourself, attach the stream to your <video>, and pass videoElement to measureVitals() — the SDK will use the existing stream.

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.

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” if lighting.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.

  • 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.fitzpatrick improves accuracy.
  • Camera quality matters less than lighting — a 720p webcam with good light beats a 4K webcam in shadow.

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 pass wasmConfig: { visionWasmUrl, visionModelUrl, geometryEngineUrl }. Contact support for the distribution package.
  • CSP: a strict Content-Security-Policy blocks the WASM/script. Allow wasm-unsafe-eval and the CDN origin in script-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.

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”.

<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.

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).

  • Check https://status.circadify.com for incidents.
  • Verify outbound HTTPS to api.circadify.com and 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.

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.

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.