You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FeatureFlags] Return the caller's default value on every provider error - #9299
FeatureFlagsModule.Evaluate returns the caller's default instead of null when no configuration has arrived.
FeatureFlagsSdk.GetResolutionDetails returns the caller's default for every evaluation that carries an error, and when the tracer is not attached.
Datadog.FeatureFlags.OpenFeature 2.3.1 → 2.3.2.
Reason for change
Before configuration arrives, GetStringValueAsync("flag", "fallback") returned null instead of "fallback", with PROVIDER_NOT_READY. That is the startup window every application passes through.
OpenFeature substitutes the caller's default only when a provider throws. This provider reports errors as resolution details instead, so the default must travel with the details. Three layers could have supplied it, and none did:
Package — GetResolutionDetails never received the default. It copied whatever value the tracer returned, and returned default! when the tracer is not attached. For a string flag that is null. It came in with the package in [FFE] OpenFeature 2.x shim package #8077.
OpenFeature client — it does not step in, because the provider does not throw.
The same gap also lost JSON defaults. On error the tracer hands back the caller's OpenFeature Value, which JsonToValue cannot convert, so the caller got an empty Value.
How we found it
system-tests #7496 enabled the Feature Flags configuration-source tests for .NET for the first time. On the first run, the five cold-start cases failed with {'errorCode': 'PROVIDER_NOT_READY', 'value': None} where they expect the default. Those tests had been missing_feature for .NET, and the unit tests on this path asserted only the error, never the value, so nothing had covered it.
Implementation details
The package is the layer that owns the rule: any evaluation with an error type resolves to the caller's default. The tracer fix is still needed on its own, because it ships with the tracer, while the package reaches customers only when they upgrade it.
Resolve<T> now takes T defaultValue instead of object?. No CallTarget instruments Resolve, so changing the signature is safe.
Test coverage
Evaluate_WhenNoConfigurationYet_ReturnsTheDefaultValue covers string, boolean, integer and numeric flags, and asserts the value. The package has no unit-test project. The system-tests cold-start cases cover the not-ready path end to end.
pavlokhrebto
changed the title
Pavlo.khrebto/ffl 2468/ffe not ready default value
[FeatureFlags] Return the caller's default value on every provider error
Sep 23, 2026
Found 0 performance improvements and 11 performance regressions! Performance is the same for 61 metrics, 0 unstable metrics, 70 known flaky benchmarks, 56 flaky benchmarks without significant changes.
Explanation
This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:
🟩 = significantly better candidate vs. baseline
🟥 = significantly worse candidate vs. baseline
We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.
If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.
Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.
More details about the CI and significant changes
You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.
CIs of the difference of means are often centered around 0%, because often changes are not that big:
---------------------------------(------|---^--------)-------------------------------->
-0.6% 0% 0.3% +1.2%
| | |
lower bound of the CI --' | |
sample mean (center of the CI) -------------' |
upper bound of the CI ----------------------'
As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).
For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:
----------------------------------------|---------|---(---------^---------)---------->
0% 1% 1.3% 2.2% 3.1%
| | | |
significant impact threshold --------------' | | |
lower bound of CI --------------' | |
sample mean (center of the CI) --------------------------' |
upper bound of CI ----------------------------------'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AI GeneratedLargely based on code generated by an AI or LLM. This label is the same across all dd-trace-* reposfeature_flagstype:bug
3 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of changes
FeatureFlagsModule.Evaluatereturns the caller's default instead ofnullwhen no configuration has arrived.FeatureFlagsSdk.GetResolutionDetailsreturns the caller's default for every evaluation that carries an error, and when the tracer is not attached.Datadog.FeatureFlags.OpenFeature2.3.1 → 2.3.2.Reason for change
Before configuration arrives,
GetStringValueAsync("flag", "fallback")returnednullinstead of"fallback", withPROVIDER_NOT_READY. That is the startup window every application passes through.OpenFeature substitutes the caller's default only when a provider throws. This provider reports errors as resolution details instead, so the default must travel with the details. Three layers could have supplied it, and none did:
null. It came in with the original SDK in [FFE] Feature Flags SDK #7896, where the error string wasNo config loaded. [Feature Flags] Add flag evaluation metrics via OpenFeature hook #8367 changed the string toPROVIDER_NOT_READYand kept thenull. The evaluator's own not-ready path already returneddefaultValue, so the two paths disagreed.GetResolutionDetailsnever received the default. It copied whatever value the tracer returned, and returneddefault!when the tracer is not attached. For a string flag that isnull. It came in with the package in [FFE] OpenFeature 2.x shim package #8077.The same gap also lost JSON defaults. On error the tracer hands back the caller's OpenFeature
Value, whichJsonToValuecannot convert, so the caller got an emptyValue.How we found it
system-tests #7496 enabled the Feature Flags configuration-source tests for .NET for the first time. On the first run, the five cold-start cases failed with
{'errorCode': 'PROVIDER_NOT_READY', 'value': None}where they expect the default. Those tests had beenmissing_featurefor .NET, and the unit tests on this path asserted only the error, never the value, so nothing had covered it.Implementation details
The package is the layer that owns the rule: any evaluation with an error type resolves to the caller's default. The tracer fix is still needed on its own, because it ships with the tracer, while the package reaches customers only when they upgrade it.
Resolve<T>now takesT defaultValueinstead ofobject?. No CallTarget instrumentsResolve, so changing the signature is safe.Test coverage
Evaluate_WhenNoConfigurationYet_ReturnsTheDefaultValuecovers string, boolean, integer and numeric flags, and asserts the value. The package has no unit-test project. The system-tests cold-start cases cover the not-ready path end to end.Other details
Jira: FFL-3325
Delivery: the tracer fix ships in the next tracer release. The package fix needs
Datadog.FeatureFlags.OpenFeature2.3.2.