Crossdeck Docs
Dashboard

Error codes

Reference 5 min read · Updated May 11, 2026

Every CrossdeckError the SDK can throw. Each entry has a stable code string, a human description, a one-sentence resolution, and a retryable flag indicating whether the SDK auto-recovers (no developer action needed) or whether the failure requires intervention. Same surface Stripe ships at stripe.com/docs/error-codes.

TL;DR

CrossdeckError shape

import { CrossdeckError } from "@cross-deck/web";

try {
  await Crossdeck.identify("");
} catch (err) {
  if (err instanceof CrossdeckError) {
    err.type;        // "invalid_request_error"
    err.code;        // "missing_user_id"
    err.message;     // human-readable
    err.requestId;   // server-issued; quote in support tickets
    err.status;      // HTTP status if the error came from an API response
    err.retryAfterMs;// server's Retry-After when present
  }
}

Eight distinct type values group errors into broad categories:

typeWhat it means
authentication_errorBad API key, missing key, expired key. Server rejected the credential.
permission_errorKey is valid but not authorised for the operation (e.g. wrong origin, wrong project).
invalid_request_errorMalformed input — missing required field, invalid format, etc.
rate_limit_errorToo many requests. Honour the retryAfterMs field.
network_errorFailure at the transport layer — DNS, TCP, TLS, timeout, abort.
version_errorThe installed SDK sends a wire format the server no longer accepts (HTTP 426). Upgrade the SDK; queued events park and backfill automatically.
internal_errorCrossdeck-side bug or transient outage. Server-issued.
configuration_errorWrong init options — caught client-side at Crossdeck.init().

Configuration errors

Thrown synchronously by Crossdeck.init(). Not retryable — fix the init options.

codeDescriptionResolution
invalid_public_key The publishable key passed to Crossdeck.init() doesn't start with cd_pub_. Copy the key from your Crossdeck dashboard → API keys page.
missing_app_id Crossdeck.init() was called without an appId. Add appId to your init options — find it in the dashboard's Apps page.
invalid_environment Crossdeck.init() requires environment: "production" | "sandbox". Pass the literal string "production" or "sandbox" — no other values are accepted.
environment_mismatch The publishable key's env prefix doesn't match the declared environment option. Either change environment to match the key prefix (cd_pub_test_ ↔ sandbox, cd_pub_live_ ↔ production), or swap the key.
not_initialized An SDK method was called before Crossdeck.init(). Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.

Validation errors

Thrown when SDK method arguments don't meet the contract. Not retryable — fix the call site.

codeDescriptionResolution
missing_user_id identify() was called with an empty userId. Pass a stable, non-empty user identifier from your auth layer — never a hardcoded placeholder.
missing_event_name track() was called without an event name. Pass a non-empty string as the first argument.
missing_group_type group() was called without a group type. Pass a non-empty type (e.g. "org", "team") as the first argument.
missing_signed_transaction_info syncPurchases() was called without StoreKit 2 signed transaction info. Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.

Network errors

Transport-layer failures. Retryable — the SDK's built-in exponential-backoff retry handles them automatically for the event queue. Manual API calls (identify, getEntitlements, syncPurchases) surface them to the caller.

codeDescriptionResolution
fetch_failed The underlying fetch() call failed (typically a network outage or DNS issue). Check the user's network. The SDK will retry automatically with exponential backoff (1s → 60s with jitter, capped at maxMs).
request_timeout A request was aborted after the configured timeoutMs (default 15s). Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.

Internal errors

Crossdeck-side bugs or transient outages.

codeDescriptionResolution
invalid_json_response The server returned a 2xx response with an unparseable body. Likely a transient backend bug. Retry; if it persists, contact support with the requestId.

Full table

The complete reference, sorted by type. The SDK exports the same list at runtime as CROSSDECK_ERROR_CODES, and ships it as a sidecar JSON for tooling.

import { CROSSDECK_ERROR_CODES, getErrorCode } from "@cross-deck/web";

CROSSDECK_ERROR_CODES;
// → ErrorCodeEntry[] — same shape as the table above

getErrorCode("environment_mismatch");
// → { code, type, description, resolution, retryable }
code type retryable Resolution summary
invalid_public_keyconfiguration_errorNoCopy the key from the dashboard.
missing_app_idconfiguration_errorNoAdd appId from the Apps page.
invalid_environmentconfiguration_errorNoPass "sandbox" or "production".
environment_mismatchconfiguration_errorNoReconcile key prefix with declared env.
not_initializedconfiguration_errorNoCall init() before any other method.
missing_user_idinvalid_request_errorNoPass a non-empty userId.
missing_event_nameinvalid_request_errorNoPass a non-empty event name.
missing_group_typeinvalid_request_errorNoPass a non-empty group type.
missing_signed_transaction_infoinvalid_request_errorNoPass the JWS from StoreKit 2.
fetch_failednetwork_errorYesSDK retries automatically.
request_timeoutnetwork_errorYesCheck network or increase timeoutMs.
invalid_json_responseinternal_errorYesRetry; contact support with requestId if persistent.
missing_api_keyauthentication_errorNoEnsure init({ publicKey }) ran with a cd_pub_… key; re-check env-vars in CI/Docker.
invalid_api_keyauthentication_errorNoCopy the key from the dashboard; confirm the cd_pub_test_ / cd_pub_live_ prefix.
key_revokedauthentication_errorNoMint a fresh key in the dashboard; the revoked one cannot be reactivated.
identity_token_invalidauthentication_errorYesRefresh the ID token client-side and retry; confirm the signer is registered.
origin_not_allowedpermission_errorNoAdd the origin under Settings → Allowed origins (wildcards supported).
bundle_id_not_allowedpermission_errorNoRegister the iOS bundle ID under Apps → your app → iOS bundle IDs.
package_name_not_allowedpermission_errorNoRegister the Android package name under Apps → your app → Android package names.
env_mismatchpermission_errorNoUse a live key with a production app and a test key with a sandbox app — the two cannot cross.
idempotency_key_in_useinvalid_request_errorNoUse a fresh key for a different body, or reuse the key only with the exact same body.
rate_limitedrate_limit_errorYesHonour the Retry-After header; throttle to <100 req/s/key.
internal_errorinternal_errorYesSDK retries automatically; contact support with the requestId if it persists.
google_not_supportedinvalid_request_errorNoGoogle Play purchases verify via Real-time Developer Notifications until v1.5+.
stripe_not_supportedinvalid_request_errorNoWire Stripe via Checkout / Customer Portal; Crossdeck reconciles via the platform webhook.
missing_required_paraminvalid_request_errorNoInspect error.message for the missing field name.
invalid_param_valueinvalid_request_errorNoRead error.message for the field and reason.
sdk_version_unsupportedversion_errorNoUpgrade to at least error.minVersion; the parked event queue backfills automatically.

Server-side errors that come back via the API (rate limits, permission denials, validation failures) follow Stripe's envelope format — { error: { type, code, message, request_id } } — and surface as CrossdeckError with the appropriate status. The code values for those are issued server-side and may extend the list above.

Machine-readable JSON

For AI assistants, error-aggregator dashboards, and any tooling that wants the table without booting a JavaScript runtime, the SDK ships a sidecar JSON file in the npm tarball:

node_modules/@cross-deck/web/dist/error-codes.json

Shape:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "generatedAt": "2026-05-11T11:23:00.000Z",
  "sdk": "@cross-deck/web",
  "codes": [
    {
      "code": "environment_mismatch",
      "type": "configuration_error",
      "description": "The publishable key's env prefix doesn't match the declared environment option.",
      "resolution": "Either change `environment` to match the key prefix (cd_pub_test_ ↔ sandbox, cd_pub_live_ ↔ production), or swap the key for one minted in the right env.",
      "retryable": false
    },
    ...
  ]
}

It is also reachable via the unpkg CDN at https://unpkg.com/@cross-deck/web@^1/dist/error-codes.json for tooling that doesn't pull the package into its dependency tree.

Handling errors in app code

Two patterns. Pick by intent.

Catch a specific code for recovery

try {
  await Crossdeck.identify(userId, { traits });
} catch (err) {
  if (err instanceof CrossdeckError && err.code === "environment_mismatch") {
    // Re-init with the right env, or surface a developer-facing toast.
  } else {
    // Unknown error — surface generically + log.
    console.error("identify failed:", err);
  }
}

Branch by category for broad strokes

if (err instanceof CrossdeckError) {
  switch (err.type) {
    case "authentication_error":
      // API key is bad — alert the developer, not the user.
      break;
    case "rate_limit_error":
      // Honour err.retryAfterMs — back off.
      break;
    case "network_error":
      // Transient. SDK auto-retries the event queue; manual calls fail.
      break;
  }
}
You don't have to catch errors from track().

Crossdeck.track() is fire-and-forget and synchronous. It never throws on a network failure — that's the queue's job. The only way track() throws is the validation case missing_event_name, which is a programmer error caught the moment you call track("").


This list reflects @cross-deck/web@1.9.1. New codes added in future releases will appear here in lockstep with the SDK source at sdks/web/src/error-codes.ts.