#4203·openreplay

"Total Events" metric counts ALL events instead of filtered event type

Author: marat-akhmetianovCreated Jan 19, 2026Updated Aug 11, 2026
Labelsbugproduct analytics

Describe the issue When using the "Total Events" metric format with an event name filter applied, the dashboard displays an inflated count that includes ALL event types occurring in matching sessions, rather than counting only the events that match the specified filter.

This means if you filter for a specific custom event (e.g., a button click), the metric incorrectly includes all other events (PERFORMANCE, ISSUE, CLICK, LOCATION, REQUEST, INPUT, etc.) that occurred in the same session.

Steps to reproduce the issue

  1. Go to Product Analytics dashboard
  2. Create a metric with an event name filter for any custom event
  3. Add any additional filters (user, date range, etc.)
  4. Select metric format: "Total Events"
  5. Observe the displayed count
  6. Compare with actual data:
    • Query the database for events matching your filter
    • Observe that the dashboard shows a much higher count than the actual filtered events
    • The count appears to include many/all event types in matching sessions

Expected behavior The "Total Events" metric should count only events that match the applied event name filter.

For example, if filtering for a custom "Submit Form" event:

  • Expected: Count only "Submit Form" events
  • Actual: Counts "Submit Form" + PERFORMANCE + ISSUE + CLICK + LOCATION + all other events in sessions containing "Submit Form"

Screenshots

  • Session UUID: ac6593a0-9888-453e-918c-622207063e77
  • Total events in session: 82 (includes PERFORMANCE, ISSUE, CLICK, LOCATION, REQUEST, and various custom events)
  • Events matching our specific filter: 4
  • Dashboard "Total Events" metric showed: 57 (incorrect - should be 4)
Image

OpenReplay Environment

  • OpenReplay version: v1.23.8
  • Cloud provider: GCP (GKE)
  • Deployment: Kubernetes/Helm
  • Frontend stack: React
  • Tracker version: Latest (embedded in React app)
  • Plugins used: Standard tracking
  • System specs: GKE cluster with multiple node pools

Additional context

Root Cause (Code Analysis)

File: https://github.com/openreplay/openreplay/blob/main/backend/pkg/analytics/charts/metric_timeseries.go#L291-L300 Lines: 291-300 (getProjectionAndJoin method)

The Bug: When MetricEventCount is used with event name filters, the query logic:

  1. Correctly finds sessions containing the filtered event (subquery) ✓
  2. Then LEFT JOINs to fetch ALL events from those sessions ✗ Bug
  3. Counts ALL event_ids instead of only the filtered events ✗ Bug

Current Code (lines 291-300):

go
case MetricEventCount:
    projection := "e.event_id AS event_id, s.datetime AS datetime"
    joinEvents := `
    LEFT JOIN product_analytics.events AS e
      ON e.session_id = evt.session_id        <- Joins ALL events!
     AND e.project_id = @project_id           <- Missing event name filter!
    `
    return projection, joinEvents

What's Missing: The event name filter from the query is not re-applied in the LEFT JOIN, so it brings in all events from matching sessions instead of just the filtered event type.

Suggested Fix:

go
case MetricEventCount:
    projection := "e.event_id AS event_id, s.datetime AS datetime"
    joinEvents := `
    LEFT JOIN product_analytics.events AS e
      ON e.session_id = evt.session_id
     AND e.project_id = @project_id`
    
    // FIX: Re-apply event name filter from eventFilters
    if len(eventFilters) > 0 {
        for _, f := range eventFilters {
            if f.Type == "EVENT_TYPE" && f.Operator == "is" {
                joinEvents += " AND e.$event_name = @event_name"
                break
            }
        }
    }
    
    if p.SampleRate > 0 && p.SampleRate < 100 {
        joinEvents += fmt.Sprintf(" AND e.sample_key < %d", p.SampleRate)
    }
    return projection, joinEvents
SQL Query Structure (Simplified)

Current (Incorrect):

sql
SELECT COUNT(DISTINCT e.event_id)
FROM (
    -- Subquery finds sessions with the filtered event
    SELECT DISTINCT evt.session_id
    FROM product_analytics.events evt
    WHERE evt.$event_name = 'YourCustomEvent'
) AS evt
LEFT JOIN product_analytics.events AS e  -- BUG: Joins ALL events in session
  ON e.session_id = evt.session_id
 AND e.project_id = @project_id
-- Missing: AND e.$event_name = 'YourCustomEvent'

Expected (Correct):

sql
SELECT COUNT(DISTINCT e.event_id)
FROM (
    SELECT DISTINCT evt.session_id
    FROM product_analytics.events evt
    WHERE evt.$event_name = 'YourCustomEvent'
) AS evt
LEFT JOIN product_analytics.events AS e
  ON e.session_id = evt.session_id
 AND e.project_id = @project_id
 AND e.$event_name = 'YourCustomEvent'  <- FIX: Re-apply the event filter
Validation Data

Example from our testing:

sql
-- Total events in session
SELECT COUNT(*) FROM product_analytics.events 
WHERE session_id = 3665302490953953101;
-- Result: 82 events (all types)

-- Events matching our filter
SELECT COUNT(*) FROM product_analytics.events 
WHERE session_id = 3665302490953953101 
  AND `$event_name` = 'OurCustomEvent';
-- Result: 4 events (only filtered type)

-- Event type breakdown in the session
SELECT `$event_name`, COUNT(*) as cnt 
FROM product_analytics.events 
WHERE session_id = 3665302490953953101 
GROUP BY `$event_name` 
ORDER BY cnt DESC;
-- Results show mix of:
-- PERFORMANCE events: 15
-- ISSUE events: 14
-- CLICK events: 13
-- LOCATION events: 12
-- REQUEST events: 12
-- Various custom events: 10, 4, 2, etc.
-- OurCustomEvent: 4  <- What we filtered for

Dashboard displayed: 57 (incorrect - should be 4)

Analysis of the count discrepancy:

  • Actual filtered events in database: 4
  • Dashboard shows: 57
  • Session total events: 82
  • Stored session.events_count (Postgres): 45

The bug is consistently counting more events than the filter should allow. The exact number (57) likely represents a subset of session events being counted, but not the correct filtered subset. The variance between 45 (stored count) and 82 (actual ClickHouse rows) suggests that some auto-captured events (PERFORMANCE, ISSUE) may not be counted in the session summary, but this is separate from the filtering bug.

The core issue: Dashboard counts 57 instead of 4 - a 14x inflation due to the missing filter in the JOIN.

Impact

This bug affects any Product Analytics query using:

  • Metric format: "Total Events"
  • With event name filters applied

Users see inflated metrics that don't match the actual filtered event count, leading to incorrect analysis and decision-making.