SDK errors

captureCDR() rejects rather than returning a partial result. If it resolves, the evidence is stored.

Errors are ExpressConsentError instances carrying a code, a message, and sometimes details. Every one is also dispatched on window as an expressconsent:error event, except CAPTURE_NOT_IN_BROWSER, where there is no window to dispatch on.

javascript
try {
  await window.ExpressConsent.captureCDR();
} catch (error) {
  console.error(error.code, error.message, error.details?.backendCode);
}

Handle them the same way

Whatever the cause, the response is the same: log it and let the person's submission proceed. Never block a consumer from submitting because evidence capture failed.

Codes

CodeCause
CONFIG_CID_MISSINGNo organization identifier: no data-ec-cid, no ?cid= on the script URL, nothing on window.__ExpressConsentConfig, and no cid passed to the call. A setup error; it will fail on every capture.
UPLOAD_FAILEDThe server rejected the upload. details.backendCode says why; see below.
INTERNAL_ERRORThe capture failed locally: a network failure, a timeout, or an error while serializing the page.
CONFIG_API_BASE_UNRESOLVEDThe upload endpoint could not be resolved, because the SDK was not served from an ExpressConsent host. Load it from sdk.expressconsent.com; a self-hosted, proxied, or bundled copy cannot work out where to upload.
CAPTURE_NOT_IN_BROWSERNo window or document. You are calling it during server-side rendering.

NETWORK_ERROR appears in the exported ExpressConsentErrorCode type but is never thrown. Network failures arrive as INTERNAL_ERROR.

error.details carries backendCode and backendMessage, and only on UPLOAD_FAILED. Route backendCode into your monitoring; it is the only thing that says why an upload was refused.

Upload rejection reasons

When the code is UPLOAD_FAILED, details.backendCode carries the server's reason.

backendCodeMeaning
CAPTURE_DISABLEDCapture is switched off for your organization. Contact us.
PAYLOAD_TOO_LARGEOver the 3 MiB limit. Usually inlineAssets left on in production, or an unusually large page.
PAYLOAD_TOO_SMALLThe upload body was effectively empty.
INTEGRITY_FAILUREThe uploaded bytes did not match their checksum, or did not match the fingerprint recorded when the capture was taken. Not retried. Usually a proxy or browser extension rewriting the request body.
IDEMPOTENCY_CONFLICTDifferent content was uploaded under a record identifier that already exists.
MISSING_HEADER, INVALID_HEADERA malformed request. Despite the names these are about the upload's query parameters, HTTP method, and content type rather than HTTP headers. Indicates a bug, or a proxy rewriting the upload.
MISSING_COMMITA stored record was re-sent but its chain of custody could not be established, so it was refused.
SERVER_ERROROur side failed. Retried automatically.
INVALID_ENVELOPE, INVALID_SNAPSHOTDefined but not currently returned. A body the server cannot decode comes back as INVALID_HEADER.

What to do about a record you never got

A capture that fails locally can still arrive later, re-sent from the browser on the person's next visit, long after your code gave up waiting and moved on without a cdrId.

Pass your own identifier in custom on every capture, and reconcile from the webhook or by filtering the API on that value.