Skip to content

Local evaluation: gt/gte/lt/lte compare numeric strings lexicographically, diverging from posthog-js and the server #1077

Closed

Description

@Harsh23Kashyap

Describe the bug

When a person property arrives as a numeric string (common when properties are captured from query params, headers, or form data), match_property in this SDK's local evaluation compares gt/gte/lt/lte operands as plain strings. posthog-js and the server both coerce numeric strings to numbers first, so the same flag and the same property produce different answers depending on which SDK evaluates them.

Reproduction

from posthog.feature_flags import match_property

def P(op, v):
    return {"key": "k", "operator": op, "value": v, "type": "person"}

print(match_property(P("gt", 2), {"k": "10"}))    # False  (lexicographic: "10" < "2")
print(match_property(P("lt", 30), {"k": "100"}))  # True   ("100" < "30" as strings)
print(match_property(P("gte", "5"), {"k": "10"})) # False

The same three filters in posthog-js (matchFeatureFlagProperty in @posthog/core, featureFlagLocalEvaluation.ts, which parses both sides as numbers):

gt 2 vs "10"   -> true
lt 30 vs "100" -> false
gte "5" vs "10" -> true

Server-side flag evaluation also coerces numeric strings before comparing (the hog VM's unifyComparisonTypes, which PostHog/posthog#59307 references as the behavior the query layer needed to match). So Python is the odd one out on both ends.

A concrete consequence: a flag with usage_count gt 2 includes a user whose usage_count is "10" when evaluated in a browser or on the server, and excludes them when evaluated locally in a Python service.

Note on existing tests

posthog/test/test_feature_flags.py::TestMatchPropertiesMathOperators appears to pin the lexicographic result for string overrides ("100" matches lt "30"), with a comment about adjusting comparison by override type. That reads as deliberate, so this may be an intentional design decision - but it leaves the Python SDK inconsistent with the other local-evaluation implementations and with the server, and flag authors have no way to see that from the flag UI. If lexicographic comparison is intended, would it make sense to align the other SDKs instead, or document the difference?

Environment

  • posthog 7.71.0
  • @posthog/core built from posthog-js main (3ddaa65)
  • Python 3.10

Activity

  1. dustinbyrne commented on Oct 19, 2026

    @dustinbyrne
    Contributor

    hey @Harsh23Kashyap, thanks for the issue. you're right about this, we should be coercing to a numeric before doing the comparison. i'll put together a fix!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions