Skip to content

fix(hogql): coerce numeric session/recording filter values#67260

Draft
posthog[bot] wants to merge 2 commits into
masterfrom
posthog-code/coerce-numeric-session-recording-filters
Draft

fix(hogql): coerce numeric session/recording filter values#67260
posthog[bot] wants to merge 2 commits into
masterfrom
posthog-code/coerce-numeric-session-recording-filters

Conversation

@posthog

@posthog posthog Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Problem

Filtering insights or session recordings by a numeric session/recording property (autocapture count, click count, etc.) returned a raw ClickHouse 500 instead of results.

The compare-op builder in posthog/hogql/property.py only coerced boolean values for session-type properties and did nothing for recording types. Numeric session/recording metric columns ($autocapture_count, click_count, …) are Int/Float in ClickHouse, so the builder emitted comparisons like equals($autocapture_count, '5') with a raw string constant. ClickHouse then failed the whole query with "Cannot convert string to Int", surfacing two ways:

  • "There is a problem with this query" in the insight editor — $autocapture_count is labelled "Numeric" in the taxonomy but the value was never cast.
  • An unhandled 500 in the session recordings list.

Both trace to one contained root cause on a code path shared by insight filtering and recordings.

Changes

  • Coerce filter values to int/float for numeric session and recording metric properties across the comparison operators (exact, is-not, lt/lte/gt/gte, between, in). Session numeric-ness is keyed off IntegerDatabaseField/FloatDatabaseField in LAZY_SESSIONS_FIELDS; recording numeric-ness off the Numeric-typed keys in the replay taxonomy group.
  • Raise a clean QueryError (a user-facing 4xx) when a numeric metric key is paired with a non-numeric value (e.g. an element label), instead of letting it reach ClickHouse as a 500.

Scope is intentionally the backend coercion on the shared HogQL path — that eliminates the raw ClickHouse error for both symptoms. The optional frontend guard (stopping a numeric metric key from being paired with a text value in the filter UI) is left out; the backend now turns that case into a clean validation error rather than a crash.

How did you test this code?

I'm an agent. I could not boot Django in this environment (no database reachable), so I did not run the full test suite. I did:

  • Add unit tests in posthog/hogql/test/test_property.py:
    • test_session_numeric_property_coercion (parameterized over exact/is-not/lt/lte/gt/gte) — locks in that a string value against $autocapture_count becomes a numeric constant, guarding the "Cannot convert string to Int" regression that no existing test covered (only boolean session coercion was tested).
    • test_recording_numeric_property_coercion — same guarantee for recording click_count.
    • test_numeric_property_non_numeric_value_raises_query_error (parameterized over session/recording) — asserts a text value paired with a numeric metric raises QueryError rather than a raw 500.
  • Validated the pure coercion logic (int/float/error branches) in isolation.
  • Ran ruff check and ruff format --check on the changed files (clean) and byte-compiled both.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

🤖 Agent context

Autonomy: Fully autonomous

Authored by an agent (Claude Code) from a PostHog inbox report. No repo skills shaped the code beyond /writing-tests, which was invoked before adding the test cases to apply the value gate.

Key decision: detect numeric session fields via LAZY_SESSIONS_FIELDS field types and numeric recording fields via the replay taxonomy group's Numeric type (mirroring the existing virtual-property boolean handling), rather than hardcoding a column list — keeping the check aligned with what the taxonomy exposes. Coercion is applied only for value-comparing operators so is_set/is_not_set and string-search operators are untouched.


Created with PostHog Code from an inbox report.

posthog Bot added 2 commits July 1, 2026 06:47
Numeric session and recording metric columns (e.g. session $autocapture_count,
recording click_count) are Int/Float in ClickHouse, but the compare-op builder
emitted the filter value as a raw string constant. ClickHouse then failed the
whole query with "Cannot convert string to Int", surfacing as a raw 500 in the
recordings list and "There is a problem with this query" in the insight editor.

Coerce the value to int/float for numeric session/recording metric properties
across the comparison operators, and raise a clean QueryError when the value
isn't numeric (e.g. a numeric metric key mistakenly paired with an element
label) instead of letting it fall through to ClickHouse.

Generated-By: PostHog Code
Task-Id: c2a6f5ce-5d71-462b-9807-04261dedf56a
Tighten `_coerce_to_number` to a scalar input type so mypy doesn't see a
`list[str]` reaching `int()`/`float()`, and cast the coerced value back to
`ValueT` at the call site — a coerced number can be a float (wider than the
legacy alias) but only flows into `ast.Constant(value: Any)`.

Generated-By: PostHog Code
Task-Id: c2a6f5ce-5d71-462b-9807-04261dedf56a
@scheduled-actions-posthog

Copy link
Copy Markdown
Contributor

This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the stale label – otherwise this will be closed in another week. If you want to permanently keep it open, use the waiting label.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants