Skip to main content

Overview

Each section below is a question developers ask about shipd, with the likely causes, how to check each one, and the fix. Links point to the page that owns the full behavior. Two checks help with most problems:
Terminal

Why isn’t my flag updating?

For Go, confirm DisablePolling is false and check LastError(). Use a bounded context with Refresh(ctx) to check a request immediately. Go does not use the WebSocket options in the TypeScript checks above. See Go reference.

Why does my flag always return the default?

The SDK never throws from an evaluation method. When it cannot resolve a value, it serves your defaults, or false, "control", 0, or {} if you did not pass one. See SDK.

Why did shipd push return 409 / exit 3?

Cause. shipd push exits with code 3 when the control plane returns 409 conflict. Usually someone changed the flag after your last shipd pull. It also happens when a pending approval request already exists for the change, or when the push contains an operation that a policy-protected environment does not support. Check. Run shipd diff to see how the server differs from your shipd.config.ts. Fix. Run shipd pull, merge your change, and push again. See Resolve a push conflict and Errors.

shipd push exited with code 5

Cause. An approval policy accepted part of the push but queued it for review. Exit code 5 means pending_approval: the queued changes are not live yet. Check. Run the push with --json and read approvalRequests, or open Approvals in the dashboard. Fix. Ask an eligible reviewer to approve the request. --yes, --ci, and --force do not bypass approval. Pushing again while the request is pending returns exit code 3. See Review production changes.

Why does the edge return 401?

Cause. The edge returns the same generic 401 for any credential it does not accept:
  • The SDK key was revoked.
  • The key is a management API key (shipd_mgmt_…) or service account token (shipd_sa_…). Only SDK keys work on the edge.
  • The key was truncated or has extra whitespace.
Check. Confirm the key starts with shipd_sdk_server_ or shipd_sdk_client_, and that it shows Active on the project’s SDK keys page. Fix. Create a new SDK key and deploy it. After a 401, the SDK keeps its last good snapshot and reports the error to onError. See SDK keys, management API keys, and service account tokens and Edge API.

TypeScript says my flag key is not assignable

Cause. The flag key is not in your generated FlagDefinitions, or it is the wrong flag type for the method (for example, isEnabled on a variant flag). Check. Open src/generated/flags.ts (or your configured typesOut) and search for the key. Check that FlagDefinitions is imported from that generated file and passed to createClient<FlagDefinitions>() or createShipdReact<FlagDefinitions>(). Fix. Regenerate types:
Terminal
shipd push and shipd pull also regenerate them. Use the method that matches the flag type: isEnabled for boolean, getVariant for variant, getNumber for number, and getJson for JSON flags. See Generated types.

Percentage rollout serves everyone the same value

Cause. A percentage condition buckets by userId (or a chosen attribute). Without it, the condition never matches, so every request falls through to the same value. Check. Log the evaluation context you pass. TypeScript server clients have no userId unless you pass one per call or in createClient({ context }). Go requires EvaluationContext per call and has no client-wide context option. Server identify() / Identify does not set evaluation context. Fix. Pass a stable userId:
See Roll out a flag to a percentage of users and Identify users.

WebSocket is not available in this runtime

Cause. You passed stream: true, but the runtime has no global WebSocket. Node.js 20 does not; Node.js 22 and later, and browsers, do. Check. onError receives shipdit: WebSocket is not available in this runtime. Polling still works. Fix. Upgrade to Node.js 22, pass a constructor from the ws package, or omit stream to poll only:
src/lib/shipd.ts
See Node.js and server runtimes.

Flags flash in the browser on load

Cause. The page renders with your defaults, then re-renders when the snapshot arrives. Check. The flash happens on a user’s first visit, or after you set persist: false. Fix. Pick one: See Browser, React, and Next.js.

What happens if shipd is down?

Flag checks keep working. Evaluation is local, so SDKs serve the last good snapshot while the edge is unreachable, and continue polling if the WebSocket gateway is down, provided polling is enabled. If the control plane is down, shipd push and the dashboard fail, but evaluation continues from the last published snapshot. See What happens if shipd is down?