Skip to content

Local flag evaluation drops users whose rollout context value is 0 or False - #192

Open
ckarnell wants to merge 1 commit into
mixpanel:masterfrom
ckarnell:fix-falsy-rollout-context
Open

Local flag evaluation drops users whose rollout context value is 0 or False#192
ckarnell wants to merge 1 commit into
mixpanel:masterfrom
ckarnell:fix-falsy-rollout-context

Conversation

@ckarnell

Copy link
Copy Markdown

Local flag evaluation reads the rollout context with a truthiness check:

if not (context_value := context.get(flag_definition.context)):

A context value of 0 or False takes the missing-key branch. The user gets the fallback, the reason attached to it is MISSING_CONTEXT_KEY, and the log reports the key as absent from a dictionary that contains it.

Falsy values are legitimate here. context_value is typed Any and the only thing done with it is normalized_hash(str(context_value), salt), so 0 buckets as "0" the same way 1 buckets as "1". The docstring describes the context as the distinct_id "and any other attributes needed for rollout evaluation", and attributes like a purchase count or a subscription boolean are falsy for a large share of users.

flag context "account_age_days", user context {"distinct_id": "u1", "account_age_days": 0}
before  ->  fallback, MISSING_CONTEXT_KEY
after   ->  the assigned variant

Anyone bucketed on a numeric or boolean property is excluded from every locally evaluated flag on that property, and the fallback reason points at the caller for an argument they passed.

Two tests added next to test_get_variant_tags_missing_context, one for 0 and one for False. Both fail on master. The existing test for an absent key still passes untouched, which is the half I wanted to be sure of. Full suite 170 passed on 3.9.

One judgement call worth your eyes: is None means an empty string now counts as present and gets hashed instead of falling back. That seemed right, since the message and the reason are both about presence, but it's your call.

A context value of 0 or False took the missing-key branch, so the user
got the fallback with reason MISSING_CONTEXT_KEY and a log line saying
the key was absent from a dictionary that contained it.

context_value is typed Any and is only used as normalized_hash(str(...)),
so falsy values bucket the same as any other.
@ckarnell
ckarnell requested review from a team and efahk August 17, 2026 12:42
@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

The changed presence check matches the missing-key fallback semantics, and the regression tests cover the reported numeric and boolean falsy cases without disturbing absent-key handling.

Important Files Changed

Filename Overview
mixpanel/flags/local_feature_flags.py Correctly narrows missing-context detection to None, allowing falsy values to participate in local rollout evaluation.
mixpanel/flags/test_local_feature_flags.py Adds focused regression tests confirming that zero and false context values are locally evaluated.

Reviews (1): Last reviewed commit: "Treat a falsy rollout context value as p..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant