> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useshipd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every shipd error in one place: CLI exit codes and error codes, control-plane API errors, edge API statuses, and SDK errors.

## Overview

This page lists every error shipd returns and how to fix it: `shipd` exit codes and error codes, the control-plane API error envelope, edge API statuses, and the errors the `shipdit` SDK throws or reports to `onError`. For symptoms such as "my flag isn't updating", start with [Troubleshooting](/resources/troubleshooting).

## CLI exit codes

| Code | Meaning | Common causes | Fix |
| - | - | - | - |
| `0` | Success | Push applied or had nothing to do; diff found no drift | — |
| `1` | General error | Not authenticated; `403 forbidden`; invalid `shipd.config.ts`; project or environment not found; confirmation declined or no TTY; unreachable API; command usage error | Read the `Error:` line. See [CLI error codes](#cli-error-codes). |
| `2` | Drift | `shipd diff --exit-code` found creates, updates, or remote-only flags | Run `shipd push`, or `shipd pull` if the server is right. |
| `3` | Conflict (`409`) | A flag changed on the server after the plan; a pending approval request already exists for the flag and environment; a definition change touches a project with an approval policy | See [Why did shipd push return a conflict?](/config-as-code/conflicts) |
| `4` | Unlinked | `shipd diff --exit-code` found unlinked flags. Current servers do not report them. | Treat like `2`. |
| `5` | Pending approval | `shipd push` queued one or more changes for review. They are not live. | Ask a reviewer to approve the request. See [Approvals](/operations/approvals). |

Exit codes are defined per command in the [CLI reference](/reference/cli#exit-codes).

## CLI error codes

Human output prints `Error: <message>` on stderr. With `--json`, stderr gets:

```json theme={null}
{ "error": { "code": "validation_error", "message": "Environment is required. Pass --env, set SHIPD_ENV, or use a management key / service-account token bound to an environment." } }
```

Errors from the API keep their [control-plane code](#control-plane-api-errors). The CLI adds these on its own:

| Code | Message | Fix |
| - | - | - |
| `validation_error` | `Project is required. Pass --project, set SHIPD_PROJECT, or use a management key / service-account token bound to a project.` | Pass `--project` or bind the credential to a project. |
| `validation_error` | `Environment is required. Pass --env, set SHIPD_ENV, or use a management key / service-account token bound to an environment.` | Pass `--env`. |
| `validation_error` | `Organization mismatch: API key is bound to "<org>", but --org/SHIPD_ORG is "<org>". …` | Remove `--org` / `SHIPD_ORG`, or use a credential for that organization. |
| `validation_error` | `Failed to load config <path>: <message>` | Fix the TypeScript or `defineFlags` error in `shipd.config.ts`. See [validation rules](/reference/config#validation-rules). |
| `validation_error` | `shipd.config.ts must export defineFlags(...) result` | `export default defineFlags({ … })`. |
| `validation_error` | `Invalid flag definitions: <path>: <message>` | Fix the named flag. |
| `validation_error` | `Push cancelled (pass --yes or --ci to apply non-interactively)` | Answer `y`, or pass `--yes` in scripts. |
| `validation_error` | `Archive cancelled` | Answer `y`, or pass `--yes`. |
| `validation_error` | `Variant flags require --variants (e.g. --variants control,treatment)` | `shipd flags create` with `--type variant` needs `--variants`. |
| `validation_error` | `--variants is only valid with --type variant (got --type <type>)` | Drop `--variants`. |
| `validation_error` | `Unsupported flag type: <type>` | Use `boolean`, `variant`, `number`, or `json`. |
| `validation_error` | Command usage error (unknown option, missing argument) | Run `shipd <command> --help`. |
| `not_found` | `Config file not found: <path>` | Run from the project root or pass `--flags-config`. |
| `not_found` | `Project not found: <slug>` / `Environment not found: <slug>` | Check `shipd projects list` / `shipd environments list`, and the credential's binding. |
| `unauthenticated` | `Device authorization timed out` / `Device authorization failed` | Run `shipd login` again and approve within the time limit. |
| `unauthenticated` | `Device authorization did not return a management API key. Approve org, project, and environment in the browser.` | Choose all three in the browser. |
| `internal` | Any unexpected error, such as a network failure | Check `--api-url` / `SHIPD_API_URL`. In CI it must be `https://api.useshipd.com`. |

## Control-plane API errors

Every non-success response from `https://api.useshipd.com/api/v1` has this envelope. Stack traces are never returned.

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "requestId": "req_01",
    "details": {}
  }
}
```

| Status | Code | Meaning | CLI exit |
| - | - | - | - |
| 401 | `unauthenticated` | Credential is missing, invalid, expired, revoked, or has no active organization. | `1` |
| 403 | `forbidden` | The principal cannot perform the action. Includes environment-bound credentials attempting `flag.create`, `flag.update`, or `flag.archive`. | `1` |
| 403 | `quota_exceeded` | An SDK or bulk quota is exhausted. | `1` |
| 404 | `not_found` | The resource is missing or hidden from this principal. | `1` |
| 409 | `conflict` | Version, uniqueness, approval, or idempotency state conflicts. | `3` |
| 413 | `payload_too_large` | A documented batch or body limit was exceeded. | `1` |
| 422 | `validation_error` | The request does not match its schema. | `1` |
| 429 | `rate_limited` | Retry after the delay in `Retry-After`. | `1` |
| 500 | `internal` | Unexpected failure. Report `requestId` to support. | `1` |
| 503 | `tenant_migrating`, `tenant_unavailable` | Organization data is temporarily unavailable. Retry safely. | `1` |

Retry, idempotency, and pagination rules are in [Control-plane API](/reference/control-plane-api).

### Push conflict messages

`shipd push` returns these `409 conflict` messages. None of them applies any part of the push.

| Message | Cause |
| - | - |
| `Flag "<key>" changed remotely (expected version <n>, remote <m>). Pull and retry, or pass --force.` | The flag definition changed after the plan. |
| `Flag "<key>" environment state changed remotely (expected environment version <n>, …). Pull and retry, or pass --force.` | The target environment's state (fallthrough, rules, enabled) changed after the plan. |
| `A pending approval request already exists for this flag in this environment` | An earlier push or dashboard edit is waiting for review. |
| `This operation affects an environment protected by approval policy and cannot be submitted for review. No changes were applied.` | A create, archive, or definition change in a project with an approval policy. |
| `Flag "<key>" was created concurrently. Pull and retry.` | Someone created the same key during your push. |

## Edge API statuses

Returned by `https://edge.useshipd.com/sdk/v1`. The SDK handles all of them without throwing.

| Status | Code | Meaning | SDK behavior |
| - | - | - | - |
| 304 | — | Snapshot unchanged since the sent `ETag` | Keeps the current snapshot. |
| 401 | — | SDK key is invalid or revoked, or the credential is not an SDK key | Keeps the last good snapshot, else defaults. Reports `snapshot fetch failed: HTTP 401`. |
| 403 | — | The SDK key lacks the scope for the route (for example, `identity:read` with a client key) | Reports the failure to `onError`. |
| 413 | `payload_too_large` | Events batch over 100 events or 256 KiB | Reports `events flush failed: HTTP 413`. |
| 422 | `validation_error` | Malformed events batch | Reports `events flush failed: HTTP 422`. |
| 429 | — | Over the per-key rate limit (6,000 requests per minute at the edge rate limiter). `Retry-After` is set. | Keeps the last good snapshot, reports `snapshot fetch failed: HTTP 429`, and retries on the next poll. |

See [Edge API](/reference/edge-api).

## SDK errors

### Thrown errors

Only these throw. Evaluation methods (`isEnabled`, `getVariant`, `getNumber`, `getJson`) never throw, and `ready()` never rejects for network errors.

| Message | Thrown by | Fix |
| - | - | - |
| `shipdit: sdkKey is required` | `createClient` | Pass `sdkKey`. Check that the environment variable is set where the code runs. |
| ``shipdit: `endpoint` was removed…`` | `createClient` | Remove `endpoint`. For shipd development and tests only, use `overrides.unsafeEndpoint`. |
| ``shipdit: `streamEndpoint` was removed…`` | `createClient` | Replace it with `stream: true`. To use another gateway, set `overrides.streamEndpoint`. |
| `shipdit: overrides.unsafeEndpoint must…` / `shipdit: overrides.streamEndpoint must…` | `createClient` | Pass an absolute URL (`https://…`; the stream also accepts `wss://`) without credentials, a query, or a fragment. |
| `shipdit: identify requires userId` | `identify` | Pass a non-empty `userId`. Use `reset()` on logout instead of `identify({ userId: "" })`. |
| `shipdit: useShipdClient must be used within ShipdProvider` | `useShipdClient` and the hooks built on it | Render the component inside `ShipdProvider`. |

### onError messages

Background failures go to the `onError` option as `Error` objects. The client keeps serving the last good snapshot, a cached or bootstrapped snapshot, or `defaults`.

| Message | Meaning |
| - | - |
| `snapshot fetch failed: HTTP <status>` | The edge returned a non-2xx status other than `304`. `401` means the key is invalid or revoked. |
| `snapshot fetch timed out` | No response within `fetchTimeoutMs` (default 10,000 ms). |
| `snapshot fetch aborted` | The request was cancelled, usually by `close()`. |
| `Unsupported snapshot schema version <n>; SDK supports version <m>` | The snapshot format is newer than this SDK. Upgrade `shipdit`. |
| `malformed snapshot ignored` | The response was not a valid snapshot. |
| `shipdit: snapshot version <v> still below notified <n> after 5 attempts` | A realtime update announced a version the edge did not serve after 5 fetches. The next poll catches up. |
| `fetch is not available in this runtime` | No global `fetch`. Pass the `fetch` option. |
| `identity fetch failed: HTTP <status>` / `identity fetch timed out` / `identity fetch aborted` | Loading remote traits after `identify()` failed. Local evaluation continues with session traits. |
| `stream ticket mint failed: HTTP <status>` | Minting a WebSocket ticket failed. Polling continues. |
| `stream ticket mint failed: invalid body` / `: missing ticket` / `: missing expiry` | The ticket response was malformed. Polling continues. |
| `stream ticket mint timed out` / `stream ticket mint aborted` | Ticket request timed out or was cancelled. |
| `shipdit: WebSocket is not available in this runtime` | `stream` is `true` but there is no `WebSocket`. On Node 20, pass the `WebSocket` option (for example, from `ws`) or omit `stream`. Polling continues. |
| `shipdit stream: <code>: <message>` | The WebSocket gateway sent an error. |
| `shipdit stream: unauthenticated` | The gateway rejected the key. The stream closes; polling continues. |
| `shipdit stream: unsupported_protocol` | The gateway does not support this SDK's stream protocol. Upgrade `shipdit`. |
| `Unsupported WebSocket protocol version <n>; SDK supports version <m>` | The gateway uses a newer stream protocol. Upgrade `shipdit`. |
| `shipdit stream: unsupported binary frame` / `shipdit stream: malformed server message` | The gateway sent a frame the SDK cannot read. The stream closes; polling continues. |
| `events flush failed: HTTP <status>` / `events flush timed out` / `events flush aborted` | Sending diagnostic events failed. Evaluation is unaffected. |

```ts TypeScript theme={null}
// src/flags.ts
import { createClient } from "shipdit";
import type { FlagDefinitions } from "./generated/flags";

export const shipd = createClient<FlagDefinitions>({
  sdkKey: process.env.SHIPD_SDK_KEY!,
  defaults: { "new-checkout": false },
  onError: (error) => console.warn("[shipd]", error),
});
```

```go Go theme={null}
// Check New and Ready errors, then inspect LastError for background request failures.
client, err := shipd.New(shipd.Options{
    SDKKey: os.Getenv("SHIPD_SDK_KEY"),
    Defaults: map[string]any{"new-checkout": false},
})
if err != nil { return err }
defer func() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    if err := client.Shutdown(ctx); err != nil { log.Printf("shipd events: %v", err) }
}()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := client.Ready(ctx); err != nil { log.Printf("using defaults: %v", err) }
if err := client.LastError(); err != nil { log.Printf("shipd request: %v", err) }
```

**Related:** [CLI reference](/reference/cli) · [Control-plane API](/reference/control-plane-api) · [Edge API](/reference/edge-api) · [SDK reference](/reference/sdk) · [Reliability and data](/reference/reliability-and-data)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.