Why is my feature flag returning the wrong type?
Last updated:
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 and no conversion happens. The dashboard then shows the flag serving one thing while your code reports another, which reads like a stale cache and is really a type mismatch.
Flags carry a type. A boolean flag serves true or false, string and number flags serve what you would expect, and a JSON flag hands back 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 dashboard is correct. So is the code. Looked at separately neither shows a problem, and only putting them together breaks anything.
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 behavior 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, where 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, acting as both the outage fallback and the shape contract for everything downstream, so populate it with real values. Code reading config.model out of an empty object tends to fail 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.
What are feature flags lists the four variation types and what each one serves. Why the default comes back at all belongs to 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.