Why is my feature flag returning the wrong type?
Last updated:
Because the SDKs do not coerce between types. If the variation holds a string and you call the boolean method, you get the default you passed rather than a converted value. The dashboard shows the flag serving one thing while your code reports another, which looks like a stale cache and is actually a type mismatch.
Flags carry a type. A boolean flag serves true or false, a string flag serves text, a number flag serves a number, and a JSON flag serves a structured object. The variation method you call has to agree with the flag you are calling it on.
When it does not, the SDK returns your default. It does not attempt a conversion, and it does not raise.
What this looks like
// Flag `checkout-copy` is a string flag serving "variant-b"client.boolVariation('checkout-copy', false); // -> false, not true
// Correct method for the typeclient.stringVariation('checkout-copy', 'control'); // -> "variant-b"The confusing part is that the dashboard is right and the code is right, in isolation. Only the pairing is wrong.
Why no coercion
Because every guess would be wrong somewhere. Is the string "false" truthy? Is an empty JSON object enabled? A rule that satisfies one codebase silently corrupts another, and a flag controlling a kill switch is the wrong place to find out.
Returning the explicit default keeps the behaviour predictable: you get the value you wrote at the call site, which is the same thing you would get during an outage.
The check
Open the flag and compare its type against the method name. Then check the variation values themselves. A boolean flag whose variations were entered as the strings "true" and "false" is a boolean flag in name only, and boolVariation will return the default for it.
This is worth looking at specifically when a flag was created through the API or Terraform rather than the dashboard, since a JSON payload makes it easy to write "true" where true was meant.
JSON flags
JSON is where this shows up most, because a JSON flag is often reached for as a config bag:
const config = client.jsonVariation('llm-config', { model: 'gpt-4o-mini', temperature: 0.2,});The default here does double duty. It is the fallback during an outage and it is the shape contract for everything downstream, so give it real values rather than an empty object. Code that reads config.model from {} fails a long way from the flag that caused it.
Types are fixed after creation
A flag’s type is set when it is created and does not change afterwards. If you need a different one, create a new flag, migrate the call sites, and retire the old key on the normal archive path.
Variation types are covered in What are feature flags, and fallback behaviour in Reliability and resilience.
Related questions
Why is my feature flag returning the default value?
Six reasons a Featureflip flag falls back to the default you passed, ordered by how often each one is the real cause, with the check for each.
How do I test code that uses feature flags?
Every Featureflip SDK ships a test client you construct with fixed flag values. No network, no initialization, no mocking the SDK surface yourself.
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.