Circadify

Troubleshooting

Common integration issues with the Circadify SDK and how to fix them.

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

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}
ini

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

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

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

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

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

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

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 the Web SDK's React bindings — they acquire the camera with permissive constraints (frameRate: { ideal: 30 }) and bind 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.

Scan Quality Issues

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

The capture finished but scan quality was too poor for reliable results. 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 very low confidence value means scan quality was poor. Prompt the user to retry.

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

Runtime Loading

WASM_LOAD_FAILED

Required SDK runtime assets could not be loaded. Causes:

  • Network: the default runtime asset host is blocked on the user's network. Contact support for approved self-hosting instructions.
  • CSP: a strict Content-Security-Policy blocks required SDK assets.
  • CORS: a self-hosted runtime asset is served without the required CORS headers.

Slow first scan

Runtime assets load on the first measureVitals() call and are cached by the browser thereafter. To prewarm, instantiate the SDK during page load.

React-specific

<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

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)

The @circadify/web-sdk package — including its React bindings at @circadify/web-sdk/react — is client-only; it touches 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 the React bindings inject "use client" into their build output).

Network / Backend

NETWORK_ERROR or UPLOAD_FAILED

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

There are two distinct causes:

  • Sandbox (ck_test_) request rate limit — best-effort throttling on the free tier when you fire too many session/start calls in a short window. This is retryable: back off with exponential delay and try again. No Retry-After header is emitted, so use your own backoff.
  • Production (ck_live_) monthly scan quota exhausted — your plan's monthly scan allotment is used up. This is not retryable within the month. Check the Usage page in the developer portal and upgrade your plan if you need a higher quota.

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.