# Error codes

> Source: https://cross-deck.com/docs/web-sdk/error-codes/ · Crossdeck developer docs (generated from the published page).

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](https://stripe.com/docs/error-codes).

## TL;DR

- Every error the SDK throws is a `CrossdeckError` with a stable `type` + `code`.

- Catch by `code` for specific recovery (`code === "environment_mismatch"`) or by `type` for broad categories (`type === "authentication_error"`).

- `retryable: true` errors are handled by the SDK's built-in retry — your code doesn't need to do anything special.

- The same list ships as a machine-readable JSON sidecar at `@cross-deck/web/error-codes.json` for AI assistants, error dashboards, and tooling.

## 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:

type What it means

authentication_error Bad API key, missing key, expired key. Server rejected the credential.
permission_error Key is valid but not authorised for the operation (e.g. wrong origin, wrong project).
invalid_request_error Malformed input — missing required field, invalid format, etc.
rate_limit_error Too many requests. Honour the retryAfterMs field.
network_error Failure at the transport layer — DNS, TCP, TLS, timeout, abort.
version_error The installed SDK sends a wire format the server no longer accepts (HTTP 426). Upgrade the SDK; queued events park and backfill automatically.
internal_error Crossdeck-side bug or transient outage. Server-issued.
configuration_error Wrong init options — caught client-side at Crossdeck.init() .

## Configuration errors

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

code Description Resolution

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.

code Description Resolution

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.

code Description Resolution

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.

code Description Resolution

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_key configuration_error No Copy the key from the dashboard.
missing_app_id configuration_error No Add appId from the Apps page.
invalid_environment configuration_error No Pass "sandbox" or "production" .
environment_mismatch configuration_error No Reconcile key prefix with declared env.
not_initialized configuration_error No Call init() before any other method.
missing_user_id invalid_request_error No Pass a non-empty userId.
missing_event_name invalid_request_error No Pass a non-empty event name.
missing_group_type invalid_request_error No Pass a non-empty group type.
missing_signed_transaction_info invalid_request_error No Pass the JWS from StoreKit 2.
fetch_failed network_error Yes SDK retries automatically.
request_timeout network_error Yes Check network or increase timeoutMs .
invalid_json_response internal_error Yes Retry; contact support with requestId if persistent.
missing_api_key authentication_error No Ensure init({ publicKey }) ran with a cd_pub_… key; re-check env-vars in CI/Docker.
invalid_api_key authentication_error No Copy the key from the dashboard; confirm the cd_pub_test_ / cd_pub_live_ prefix.
key_revoked authentication_error No Mint a fresh key in the dashboard; the revoked one cannot be reactivated.
identity_token_invalid authentication_error Yes Refresh the ID token client-side and retry; confirm the signer is registered.
origin_not_allowed permission_error No Add the origin under Settings → Allowed origins (wildcards supported).
bundle_id_not_allowed permission_error No Register the iOS bundle ID under Apps → your app → iOS bundle IDs.
package_name_not_allowed permission_error No Register the Android package name under Apps → your app → Android package names.
env_mismatch permission_error No Use a live key with a production app and a test key with a sandbox app — the two cannot cross.
idempotency_key_in_use invalid_request_error No Use a fresh key for a different body, or reuse the key only with the exact same body.
rate_limited rate_limit_error Yes Honour the Retry-After header; throttle to <100 req/s/key.
internal_error internal_error Yes SDK retries automatically; contact support with the requestId if it persists.
google_not_supported invalid_request_error No Google Play purchases verify via Real-time Developer Notifications until v1.5+.
stripe_not_supported invalid_request_error No Wire Stripe via Checkout / Customer Portal; Crossdeck reconciles via the platform webhook.
missing_required_param invalid_request_error No Inspect error.message for the missing field name.
invalid_param_value invalid_request_error No Read error.message for the field and reason.
sdk_version_unsupported version_error No Upgrade 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`.
