Project
Troubleshooting
Before changing a rollout to fix a result, check what the application passed, what the target selected and which datafile it has loaded.
Start with lint and tests#
Run these commands from your Featurevisor project:
npx featurevisor lintnpx featurevisor testLint checks definitions and test specs, and test runs the assertions. Fix lint errors before reading test results. In CI, run them as separate steps that stop on failure.
The examples below use checkout from the Quick start, on for every Dutch user and 25% of everyone else. Substitute your own feature and context.
A feature is unexpectedly off#
Ask why, with the same context your application passes:
npx featurevisor evaluate \ --feature=checkout \ --context='{"userId":"9b30","country":"de"}' \ --explainValue: falseExplanation: No rule's conditions matched, or the bucket is above the first matching rule's percentage. A bucket above the percentage does not fall through to later rules.Rollout: rule everyone matched, but bucket 67.208 is above its 25% rolloutBucket: 67208 / 100000 · key "9b30.checkout"9b30 is German, so the everyone rule applies, and their bucket of 67.2 is outside its 25%. The reason is no_match: the rollout is working, and this user is not in it yet. The explanation comes from your local definitions, not from the datafile the application has loaded, and it does not trace every check.
Check the context and segments#
A missing attribute, a value of another type or an unexpected value can stop a segment matching. Compare the context with the attributes and segments. The number 123 is not the string '123', and 'NL' is not 'nl'.
Check the bucketing attributes#
Pass every attribute the feature's bucketBy names. A missing one does not switch the feature off. Instead, everyone without it shares one bucket:
npx featurevisor evaluate \ --feature=checkout \ --context='{"country":"de"}' \ --explainWithout userId, the bucket key is checkout alone, and its bucket of 5.6 is inside 25%, so every German user without an ID gets the new checkout. See Bucketing.
Check prerequisites#
The reason conditions_unmet means a flag or global variable's root gate did not match. The gate can contain attribute, segment, feature and variable leaves. Inspect evaluation.conditions for the failed expression. Variation and feature variable results keep their normal disabled reasons. Review conditions and evaluate the referenced values with the same context.
An explanation can include separate prerequisite probes. Those probes are supporting evidence, not a trace of every check the original evaluation performed.
Check the target#
A known feature can be absent from the selected target's datafile. In that case, evaluation reports feature_not_found. Inspect the target selection and the datafile loaded by the application:
npx featurevisor list --features --target=all --jsonReplace all with the application's target and pass the same --target to evaluate. An unknown project key is different from a known feature excluded by a target. The CLI rejects unknown project keys with unknown_feature or unknown_variable. See Targets.
Check the loaded datafile#
If local evaluation is correct, compare the application's loaded revision and datafile content with the deployed file. The SDK may still hold a cached response or bundled fallback. Continue with changes not reaching users.
Two SDKs disagree#
Compare the full evaluation context, including instance context and per call overrides. Check that both SDKs load the same datafile content and revision, and use the same target, environment and set. Also compare sticky values, module callbacks and supported SDK versions.
Run the explanation command above with that context. If it disagrees with the application, first confirm that local definitions match the deployed datafile. evaluate --explain evaluates the local project, so it cannot prove what a remote application loaded. See supported SDK versions before adopting new datafile capabilities.
A change does not reach users#
Check each stage from publishing to loading:
- If the hosted file still has old values, the new datafile was not deployed, or a CDN serves a cached copy. Compare its content and revision with the build output, and review the deployment logs and cache headers.
- If the hosted file is current but the SDK is not, the application's refresh has not run or has failed. Check its refresh interval, request logs and loaded revision, and load the current datafile again.
- If only some versions of an application stay behind, they use a bundled datafile without refreshing it. Publish an application update, or add a refresh.
- If a refresh completed but nothing changed, the SDK may have rejected the datafile, or the instance was closed. Check the setter's result and the SDK's diagnostics, and fix the input or lifecycle error they report.
setDatafile returns true for an accepted update, including unchanged content, and false for a rejected update or a closed instance. The previous valid datafile remains available after rejection. Method spelling follows each SDK's language conventions. An accepted update alone does not prove that an evaluated value changed.
Merging remains the default. If the refresh represents a complete replacement, use your SDK's replacement option so definitions removed from the new datafile do not remain from an earlier merge. See SDK guides and deployment.
A build reports invalid saved allocation#
The invalid_saved_allocation error identifies the feature and rule whose saved allocation is invalid. JSON error details also include stateFile when the allocation file is known. Inspect that file and its version history.
Restore valid allocation state, or deliberately rename the rule to start a new allocation. A new allocation can change user cohorts. Do not delete unrelated state or clear the whole state directory. See when a saved allocation is invalid before rebuilding.
Tests pass locally but fail in CI#
Compare the checked out definitions, saved state, CLI version and command arguments. Ensure local and CI runs use the same set, target, environment and test filters. A local filtered run does not cover the full CI selection.
List the expanded cases for the same selection and compare them with the CI report:
npx featurevisor versionnpx featurevisor lintnpx featurevisor list --tests --apply-matrix --jsonnpx featurevisor test --jsonRepeat the CI command locally, including its selectors. Test specifications choose their environments, while --set and repeatable --target options affect the selected tests and datafiles. Use test reports and exact reruns to rerun a failing case. Always run linting first, even when the selected assertions pass.
Related#
- CLI explanations, for everything
--explainreports - Evaluation flow
- Deployment

