Skip to content

fix(flags): deserialize cohorts payload for local evaluation - #187

Merged
slshults merged 5 commits into
mainfrom
posthog-self-driving/fixflags-deserialize-cohorts-correctly-ff65ce
Aug 7, 2026
Merged

fix(flags): deserialize cohorts payload for local evaluation#187
slshults merged 5 commits into
mainfrom
posthog-self-driving/fixflags-deserialize-cohorts-correctly-ff65ce

Conversation

@posthog

@posthog posthog Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

  • Who's hurt: any Rust SDK user whose project has cohorts (most non-trivial projects) is fully blocked from local flag evaluation — with no workaround short of falling back to remote flag calls.
  • Total, not partial, failure: LocalEvaluationResponse.cohorts was typed as HashMap<String, Cohort> where Cohort required id, name, and properties. None of those exist in the payload/flags/definitions/?send_cohorts maps each cohort ID straight to a bare property group, {"type": "AND"|"OR", "values": [...]}. serde fails with missing field id, and because that kills the whole response, every flag fails to load, not just cohort-targeted ones.
  • Shipped everywhere: present since the struct landed (incl. 0.20.0 / 0.21.0). Never caught because every test fixture used "cohorts": {} — the non-empty path was never exercised.

Changes

  • Cohort now matches the wire format — it's #[serde(transparent)] over the raw property group, and the owning cohort ID is the map key (dropping the invented id/name).
  • Cohort matching recurses properlymatch_property_group walks nested AND/OR groups and cohort references (honoring negation), mirroring posthog-python's match_property_group. This replaces the old flatten-to-a-flat-Vec<Property>-and-AND logic, which threw away the group type and kept only the first property of any nested group via .next() — silently wrong for OR and nested groups even once it parsed.
  • Cohort IDs are accepted as either JSON string or number (top-level flag property vs nested cohort filter).

💚 How did you test it?

  • New integration test deserializes a real non-empty cohorts payload (nested AND/OR) end-to-end through the cache and evaluator — the exact path that used to fail — so "cohorts": {} fixtures can't hide this again.
  • New unit tests cover OR groups, nested AND-of-OR, and cohort-referencing-cohort with negation.
  • cargo test --lib feature_flags (86) and cargo test --test test_local_evaluation (22) pass; cargo fmt/clippy clean. (Pre-existing error_tracking symbolication test failures are unrelated and present on main in this environment.)

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

Created with PostHog Desktop from this inbox report.

The `/flags/definitions/?send_cohorts` response maps each cohort ID
straight to its property group (`{"type": "AND"|"OR", "values": [...]}`),
but `Cohort` required non-existent `id`/`name`/`properties` fields. serde
failed on any non-empty `cohorts` map, which took down the entire
`LocalEvaluationResponse` — so no flags loaded at all for projects with
cohorts.

`Cohort` is now transparent over the raw property group, and cohort
matching recurses through nested AND/OR groups and cohort references
(honoring `negation`) instead of flattening them and keeping only the
first property of any nested group.

Generated-By: PostHog Code
Task-Id: c95f4271-0d14-497f-b62a-49f1e8c919bb
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

posthog-rs-v0 Compliance Report

Date: 2026-08-07 07:26:30 UTC
Duration: 15392ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 103ms
Format Validation.Event Has Uuid 114ms
Format Validation.Event Has Lib Properties 109ms
Format Validation.Distinct Id Is String 107ms
Format Validation.Token Is Present 110ms
Format Validation.Custom Properties Preserved 109ms
Format Validation.Event Has Timestamp 109ms
Retry Behavior.Retries On 503 5113ms
Retry Behavior.Does Not Retry On 400 2110ms
Retry Behavior.Does Not Retry On 401 2109ms
Retry Behavior.Respects Retry After Header 5078ms
Retry Behavior.Implements Backoff 15083ms
Retry Behavior.Retries On 500 5077ms
Retry Behavior.Retries On 502 5077ms
Retry Behavior.Retries On 504 5077ms
Retry Behavior.Max Retries Respected 15080ms
Deduplication.Generates Unique Uuids 78ms
Deduplication.Preserves Uuid On Retry 5019ms
Deduplication.Preserves Uuid And Timestamp On Retry 10030ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 5026ms
Deduplication.No Duplicate Events In Batch 20ms
Deduplication.Different Events Have Different Uuids 19ms
Compression.Sends Gzip When Enabled 18ms
Batch Format.Uses Proper Batch Structure 20ms
Batch Format.Flush With No Events Sends Nothing 19ms
Batch Format.Multiple Events Batched Together 100ms
Error Handling.Does Not Retry On 403 2076ms
Error Handling.Does Not Retry On 413 2076ms
Error Handling.Retries On 408 5074ms

Feature_Flags Tests

17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 60ms
Request Payload.Flags Request Uses V2 Query Param 52ms
Request Payload.Flags Request Hits Flags Path Not Decide 24ms
Request Payload.Flags Request Omits Authorization Header 21ms
Request Payload.Token In Flags Body Matches Init 21ms
Request Payload.Groups Round Trip 23ms
Request Payload.Groups Default To Empty Object 33ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 34ms
Request Payload.Disable Geoip Omitted Defaults To False 42ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 38ms
Request Lifecycle.No Flags Request On Init Alone 17ms
Request Lifecycle.No Flags Request On Normal Capture 41ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 36ms
Request Lifecycle.Mock Response Value Is Returned To Caller 32ms
Retry Behavior.Retries Flags On 502 235ms
Retry Behavior.Retries Flags On 504 235ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 39ms

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

posthog-rs-v1 Compliance Report

Date: 2026-08-07 07:27:52 UTC
Duration: 22991ms

✅ All Tests Passed!

111/111 tests passed


Capture_V1 Tests

94/94 tests passed

View Details
Test Status Duration
Endpoint And Method.Targets V1 Endpoint 140ms
Endpoint And Method.Does Not Use Legacy Endpoints 140ms
Required Headers.Has Authorization Bearer Header 139ms
Required Headers.Has Content Type Json 139ms
Required Headers.Has Posthog Sdk Info Format 138ms
Required Headers.Has Posthog Attempt Header 138ms
Required Headers.Has Posthog Request Id 138ms
Required Headers.Has Posthog Request Timestamp 142ms
Required Headers.Has User Agent 135ms
Body Format.Body Has Created At And Batch 138ms
Body Format.No Api Key In Body 143ms
Body Format.No Sent At In Body 144ms
Event Format.Event Has Required Root Fields 144ms
Event Format.Event Uuid Is Valid 141ms
Event Format.Event Timestamp Is Rfc3339 142ms
Event Format.Distinct Id Is String 143ms
Event Format.Distinct Id At Root Not Properties 143ms
Event Format.Custom Properties Preserved 147ms
Event Format.Set Properties Preserved 141ms
Event Format.Set Once Properties Preserved 139ms
Event Format.Groups Properties Preserved 121ms
Event Format.Sdk Generates Uuid If Not Provided 123ms
Event Format.Event Has Required Root Fields Batch 155ms
Event Format.Event Uuid Is Valid Batch 155ms
Event Format.Event Timestamp Is Rfc3339 Batch 167ms
Event Format.Distinct Id Is String Batch 155ms
Event Format.Distinct Id At Root Not Properties Batch 166ms
Event Format.Custom Properties Preserved Batch 166ms
Event Format.Set Properties Preserved Batch 166ms
Event Format.Set Once Properties Preserved Batch 150ms
Event Format.Groups Properties Preserved Batch 139ms
Event Format.Sdk Generates Uuid If Not Provided Batch 145ms
Batch Behavior.Multiple Events In Single Batch 193ms
Batch Behavior.Batch Envelope Smoke 169ms
Batch Behavior.Flush With No Events Sends Nothing 104ms
Batch Behavior.Flush At Triggers Batch 1129ms
Batch Behavior.Created At Reflects Batch Creation Time 105ms
Deduplication.Generates Unique Uuids 180ms
Deduplication.Different Events Same Content Different Uuids 133ms
Deduplication.Preserves Uuid On Retry 5109ms
Deduplication.Preserves Timestamp On Retry 5066ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 5104ms
Deduplication.No Duplicate Events In Batch 140ms
Header Behavior On Retry.Attempt Header Starts At One 71ms
Header Behavior On Retry.Attempt Header Increments On Retry 10064ms
Header Behavior On Retry.Request Id Preserved On Retry 5076ms
Header Behavior On Retry.Different Requests Have Different Request Ids 2076ms
Header Behavior On Retry.Request Timestamp Changes On Retry 5061ms
Response Format Validation.Success Response Has Uuid Keyed Results 56ms
Response Format Validation.Success Response Has Ok For Each Event 38ms
Response Format Validation.Success No Retry After When All Ok 40ms
Response Format Validation.Success Retry After Present When Retry Events 29ms
Response Format Validation.Success No Retry After When Drop Only 30ms
Response Format Validation.Response Echoes Request Id 30ms
Retry Behavior.Retries On 408 5039ms
Retry Behavior.Retries On 500 5025ms
Retry Behavior.Retries On 503 5022ms
Retry Behavior.Retries On 504 5025ms
Retry Behavior.Retryable Errors Have Retry After 2024ms
Retry Behavior.Respects Retry After On Retryable Error 8022ms
Retry Behavior.Does Not Retry On 400 2028ms
Retry Behavior.Does Not Retry On 401 2026ms
Retry Behavior.Does Not Retry On 402 2026ms
Retry Behavior.Does Not Retry On 413 2028ms
Retry Behavior.Does Not Retry On 415 2025ms
Retry Behavior.Non Retryable Errors Have No Retry After 2023ms
Retry Behavior.Implements Backoff 15030ms
Retry Behavior.Max Retries Respected 15023ms
Partial Batch Handling.Handles 200 Full Success 2027ms
Partial Batch Handling.Handles 200 With All Ok 3036ms
Partial Batch Handling.Does Not Retry Dropped Events 3028ms
Partial Batch Handling.Does Not Retry Limited Events 3030ms
Partial Batch Handling.Prunes Ok Events On Partial Retry 5031ms
Partial Batch Handling.Prunes Dropped Events On Partial Retry 5023ms
Partial Batch Handling.Retries Only Retry Events From Partial 5026ms
Partial Batch Handling.Partial Retry Preserves Uuids 5023ms
Partial Batch Handling.Partial Retry Attempt Header Increments 5030ms
Partial Batch Handling.Partial Retry Request Id Preserved 5034ms
Partial Batch Handling.Respects Retry After On Partial 5022ms
Partial Batch Handling.Unknown Result Treated As Terminal 3025ms
Partial Batch Handling.Mixed Ok Drop Limited No Retry 3032ms
Compression.Sends Gzip Content Encoding 32ms
Compression.No Content Encoding When Disabled 21ms
Compression.Compressed Body Is Decompressible 21ms
Error Handling.Does Not Retry On Unknown 4Xx 2023ms
Event Options.Cookieless Mode Override 23ms
Event Options.Disable Skew Correction Override 21ms
Event Options.Process Person Profile Override 21ms
Event Options.Product Tour Id Override 22ms
Event Options.Unset Options Omitted 21ms
Event Options.Options Override In Batch 22ms
Geoip And Historical Migration.Geoip Disable Injected Into Properties 22ms
Geoip And Historical Migration.Historical Migration Set In Body 21ms
Geoip And Historical Migration.Historical Migration Absent By Default 21ms

Feature_Flags Tests

17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 15ms
Request Payload.Flags Request Uses V2 Query Param 16ms
Request Payload.Flags Request Hits Flags Path Not Decide 15ms
Request Payload.Flags Request Omits Authorization Header 14ms
Request Payload.Token In Flags Body Matches Init 16ms
Request Payload.Groups Round Trip 15ms
Request Payload.Groups Default To Empty Object 15ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 14ms
Request Payload.Disable Geoip Omitted Defaults To False 14ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 14ms
Request Lifecycle.No Flags Request On Init Alone 11ms
Request Lifecycle.No Flags Request On Normal Capture 22ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 16ms
Request Lifecycle.Mock Response Value Is Returned To Caller 14ms
Retry Behavior.Retries Flags On 502 219ms
Retry Behavior.Retries Flags On 504 216ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 22ms

@slshults

slshults commented Aug 6, 2026

Copy link
Copy Markdown
Member

The `Cohort` struct no longer carries the invented `id`/`name` fields — it
is now transparent over the raw cohort property group. Regenerate the
checked-in public API snapshot accordingly so the Public API check passes.

Generated-By: PostHog Code
Task-Id: c95f4271-0d14-497f-b62a-49f1e8c919bb
@slshults
slshults marked this pull request as ready for review August 6, 2026 23:44
@slshults
slshults requested a review from a team as a code owner August 6, 2026 23:44
@slshults
slshults requested a review from a team August 6, 2026 23:45
Comment thread src/feature_flags.rs Outdated
Comment thread src/feature_flags.rs Outdated
Comment thread src/feature_flags.rs Outdated
@marandaneto
marandaneto requested a review from a team August 7, 2026 06:43
@slshults
slshults merged commit cf8952a into main Aug 7, 2026
25 checks passed
@slshults
slshults deleted the posthog-self-driving/fixflags-deserialize-cohorts-correctly-ff65ce branch August 7, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants