#6972·growthbook

Daily Participation + conversion window: broken SQL on Redshift due to TIMESTAMP type mismatch

Author: ben-addCreated Sep 15, 2026Updated Sep 15, 2026

Describe the bug

The Daily Participation metric type generates invalid SQL on Redshift when a conversion window is configured. Without a conversion window this issue does not occur. When a conversion window is set, the __userMetricAgg CTE casts the exposure timestamp to TIMESTAMP WITHOUT TIME ZONE using CAST(MIN(umj.timestamp) AS TIMESTAMP), then passes it into LEAST() alongside CURRENT_TIMESTAMP, which Redshift always returns as TIMESTAMP WITH TIME ZONE (TIMESTAMPTZ). This causes a type mismatch that Redshift cannot resolve, and the query fails.

Error message:

function pg_catalog.date_diff("unknown", timestamp without time zone, timestamp with time zone) does not exist

Steps to reproduce

  1. Create a Daily Participation fact metric with a 7-day conversion window
  2. Add it to any experiment
  3. Run the experiment results query against a Redshift data source
  4. Query fails with the above error

Generated SQL (problematic section):

__userMetricAgg as (
  SELECT
    umj.variation,
    umj.user_id,
    COUNT(DISTINCT umj.m0_value)::float / GREATEST(
      datediff(
        day,
        CAST(MIN(umj.timestamp) AS TIMESTAMP),        -- ← strips timezone, returns TIMESTAMP
        LEAST(
          CURRENT_TIMESTAMP,                           -- ← always TIMESTAMPTZ on Redshift
          CAST(MIN(umj.timestamp) AS TIMESTAMP) + INTERVAL '168 hours'
        )
      ) + 1,
      1
    )::float AS m0_value
  FROM __userMetricJoin umj
  GROUP BY umj.variation, umj.user_id
)

Expected behaviour

The query runs successfully and returns Daily Participation results.

Actual behaviour

Query fails with:

function pg_catalog.date_diff("unknown", timestamp without time zone, timestamp with time zone) does not exist
Proposed fix

Remove the explicit CAST(... AS TIMESTAMP) calls so the timestamp retains its original type (TIMESTAMPTZ) throughout, making it consistent with CURRENT_TIMESTAMP:

__userMetricAgg as (
  SELECT
    umj.variation,
    umj.user_id,
    COUNT(DISTINCT umj.m0_value)::float / GREATEST(
      datediff(
        day,
        MIN(umj.timestamp),
        LEAST(
          CURRENT_TIMESTAMP,
          MIN(umj.timestamp) + INTERVAL '168 hours'
        )
      ) + 1,
      1
    )::float AS m0_value
  FROM __userMetricJoin umj
  GROUP BY umj.variation, umj.user_id
)

Environment

Data source: Amazon Redshift Metric type: Daily Participation Assignment query timestamp column: collector_tstamp (Snowplow unified events table, stored as UTC)