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.
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
| Code | Cause |
|---|---|
CONFIG_CID_MISSING | No 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_FAILED | The server rejected the upload. details.backendCode says why; see below. |
INTERNAL_ERROR | The capture failed locally: a network failure, a timeout, or an error while serializing the page. |
CONFIG_API_BASE_UNRESOLVED | The 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_BROWSER | No 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.
If the bundle has not run, window.ExpressConsent is undefined and the property access throws a
TypeError, which the same try/catch handles. The same happens when the script has not yet
finished loading: with async, a very early submission can beat it. Guard with
if (window.ExpressConsent) if your form can be submitted in the first moments of page load.
Upload rejection reasons
When the code is UPLOAD_FAILED, details.backendCode carries the server's reason.
backendCode | Meaning |
|---|---|
CAPTURE_DISABLED | Capture is switched off for your organization. Contact us. |
PAYLOAD_TOO_LARGE | Over the 3 MiB limit. Usually inlineAssets left on in production, or an unusually large page. |
PAYLOAD_TOO_SMALL | The upload body was effectively empty. |
INTEGRITY_FAILURE | The 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_CONFLICT | Different content was uploaded under a record identifier that already exists. |
MISSING_HEADER, INVALID_HEADER | A 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_COMMIT | A stored record was re-sent but its chain of custody could not be established, so it was refused. |
SERVER_ERROR | Our side failed. Retried automatically. |
INVALID_ENVELOPE, INVALID_SNAPSHOT | Defined 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.