Why is my feature flag returning the default value?
Last updated:
Almost always because the SDK cannot find the flag, not because evaluation failed. The usual causes, in order: the SDK key belongs to a different environment, the flag is off in that environment, the key is misspelled, the client has not finished initializing, the requested type does not match the variation type, or the flag is not marked client-side visible and you are asking from a browser.
Every variation call takes a default as a required argument, and the SDK returns it whenever the flag is missing, the value has an unexpected type, the client has not finished loading, or evaluation errors. That design is deliberate. It means an outage cannot take your app down. It also means a configuration mistake looks exactly like a healthy fallback, because both paths return the same value.
Work through these in order. The first two account for most reports.
1. The SDK key belongs to a different environment
Each environment has its own SDK key, and the key decides which configuration your process receives. A production key will never see a flag you only created in development. Nothing errors, because from the SDK’s point of view the flag genuinely does not exist.
Print the key prefix your process actually loaded:
console.log(process.env.FEATUREFLIP_SDK_KEY?.slice(0, 18));// sdk_server_a1b2c3 -> server key// sdk_client_a1b2c3 -> client keyThen confirm in the dashboard that the key belongs to the environment you are looking at. A stale value in a deploy config or a .env file that shadows the real one is the single most common cause.
2. The flag is off in that environment
Flags are configured per environment. On in development and off in production is the normal state during a rollout, and a disabled flag serves its off variation everywhere it is disabled.
3. The key is misspelled
Flag keys are exact strings. new-checkout and new_checkout are two different flags, and only one of them exists. The SDK reports this as flag-not-found rather than raising.
4. The client has not finished initializing
Server SDKs load the full configuration once at startup. Read a flag before that completes and you get the default, correctly, because there is nothing else to serve yet.
await client.initialize();// every variation call after this point reads from memoryIn a browser or React app the equivalent is the readiness flag the SDK exposes. Gate on it before you trust a value.
5. The type does not match
Asking for a boolean from a string flag returns the default rather than coercing. Check that the variation type in the dashboard matches the method you are calling.
6. The flag is not client-side visible
This one only affects browser, React, Flutter, Android and Swift SDKs. A flag has to be explicitly marked client-side visible before a client key can see it, and the SDK reports the same flag-not-found it would for a key that does not exist. There is a separate answer covering exactly that case.
One thing this is usually not
An unmet prerequisite. When a prerequisite is not satisfied the flag serves its off variation, which is a value you configured, rather than the default you passed at the call site. If you are seeing the default specifically, prerequisites are not the explanation. The reason string tells you which happened: prerequisite-failed against flag-not-found.
Full behaviour under failure is documented in Reliability and resilience, and per-environment configuration in Environments.
Related questions
Why does my flag work in dev but not production?
Flags are configured per environment, so enabling one in development changes nothing elsewhere. Rules and rollout percentages are per environment too.
Why can't my browser SDK see a feature flag?
A flag has to be marked client-side visible before a client SDK key can receive it. Server keys see every flag, client keys see only the opted-in ones.
What if I archive a flag still used in my code?
An archived flag leaves the SDK configuration, so any remaining call site falls back to the default you passed. Remove the code first, then archive.
Still stuck?
The docs cover every SDK, and the free Solo plan is enough to reproduce most of these locally.