Repository navigation
Description
Bug Description
The Rust feature-flags service makes dynamic cohort membership depend on the order of children around an unsupported group type. A decisive supported sibling can suppress the error only if it appears before the unsupported group.
This was discussed in SDK-specs #207, which proposes making the current backend behavior a cross-SDK contract. This review suggests fixing the backend instead: treat an unsupported group as unknown, let supported siblings decide when possible, and fail only if the requested cohort remains unresolved.
This issue concerns rust/feature-flags in this repository, not the Rust SDK, and does not request implementing XOR semantics.
How to reproduce
-
Evaluate a dynamic cohort with these filters using
evaluate_dynamic_cohorts:{ "properties": { "type": "OR", "values": [ {"type": "XOR", "values": []}, {"type": "AND", "values": []} ] } }An empty supported AND group is true. With the XOR child first, evaluation returns
Err(FlagError::CohortFiltersParsingError). -
Reverse the two children. Evaluation returns
Ok(true). -
Repeat with an AND parent and an empty OR child, which is false. Unsupported-first errors; false-first returns
Ok(false).
The same issue occurs with populated XOR groups and at both root-supported-group and recursively nested-supported-group evaluation levels.
Expected behavior
Let X be an unsupported XOR group, T a supported matching child, and F a supported nonmatching child:
| Children | Expected result |
|---|---|
OR[X,T] and OR[T,X] |
true |
AND[X,F] and AND[F,X] |
false |
OR[X,F] and OR[F,X] |
unresolved; retain the existing outward evaluation error |
AND[X,T] and AND[T,X] |
unresolved; retain the existing outward evaluation error |
Preserve decisive short-circuiting and supported empty-group behavior. An unsupported group is unknown even when its values are empty. Unknown must not become false merely to let not_in or leaf negation turn it into a match.
Additional context
Relevant backend paths
Source links are pinned to investigated master 9a739f430d1d444b4a39e2908cfaf45cbe3c6b23:
- Root and nested group evaluation: both evaluators immediately propagate child errors with
?; an unsupported nested group returnsCohortFiltersParsingError. - Dynamic cohort dependency evaluation: dependencies are evaluated before parents, with boolean-only results and immediate error propagation.
- Group models: unknown root group types currently fail decoding, whereas nested group types are strings.
Implementation considerations
Changing the group loops alone fixes the inline examples but is not sufficient for referenced cohorts:
- Preserve an internal unknown state separately from both definitive booleans and unrelated fatal errors. A cohort-local enum or
Result<Option<bool>, FlagError>could do this. - Carry unknown through referenced-cohort results,
not_in, and leaf negation. Otherwise an unresolved dependency still aborts before its parent can use a decisive sibling. The generic dependency graph already supports different result types; a general graph rewrite should not be necessary. - Recognize unsupported root types as unknown. During dependency extraction, treat unsupported groups as opaque, rather than letting references inside them introduce unrelated dependency failures before supported siblings can decide the enclosing group.
- Convert a remaining unknown to the existing error only at the requested-cohort boundary. Do not broadly swallow every
CohortFiltersParsingError: it also represents malformed known leaves and exceeded recursion bounds. - Keep this scoped to cohort membership. Changing composition across separate ordered release conditions or variant assignments would require separate consideration.
Regression coverage
Extend the existing unknown-group test with both child orders, empty/populated unsupported groups, nested parents, referenced cohorts with unsupported roots, and negation. Keep its lone-XOR error case: a cohort that cannot be decided should still fail. Preserve supported-group, static-membership, malformed-known-leaf, cycle and recursion-limit coverage.
Debug info
- Source investigation against master
9a739f430d1d444b4a39e2908cfaf45cbe3c6b23. - A group-only isolated Rust probe using the exact production bodies of the two group evaluators and item dispatcher reproduced the order dependence. Supporting group types were structural stand-ins; this was not full-service, decoder or dependency-graph execution.
- Dependency/decoder considerations above were source-traced. No production impact estimate or full-service regression validation is claimed.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
marandaneto commentedon Nov 11, 2026
when this is done, the SDKs should:
lemme know and i can double check the SDKs
posthog commentedon Nov 11, 2026
PostHog self-driving picked up this issue. Status: Research is done.
Somebody may already be working on it, so ask before you start a fix.
Report (team access only): https://us.posthog.com/project/2/inbox/reports/01a1ca1c-b77c-721c-9213-d89ae429115e?link_source=github_comment
fix(flags): let siblings decide cohorts with unsupported group types (#…