In GA4 BigQuery, you must be aware of the following caveats when calculating sessions.

In GA4, a session can start with a ‘session_start’ event or ‘user_engagement’ event.
Both the ‘session_start’ event and the ‘user_engagement’ event contain the ‘ga_session_id’ event parameter.
So if you calculate sessions by counting each unique combination of ‘user_pseudo_id’ and ‘event_timestamp’ (where ‘event_name’=’session_start’), you will discount all those sessions which did not start with the ‘session_start’ event.
Similarly, if you calculate sessions by counting each unique combination of ‘user_pseudo_id’ and ‘event_timestamp’ (where ‘event_name’=’user_engagement’), you will discount all those sessions which started with the ‘session_start’ event.
Since the ‘ga_session_id’ event parameter is the timestamp of when a GA4 session begins, it is possible that multiple sessions from different users all start at the exact same time, esp. for high-traffic websites.
In such a situation, multiple sessions from different users can have the same ‘ga_session_id’.
So, if you calculate sessions by counting the unique values of ‘ga_session_id’, you will underreport on sessions as multiple users can have the same ‘ga_session_id’.
In many cases, a session can still be inferred to have started without explicitly seeing either the ‘session_start’ event or the ‘user_engagement’ event in the BigQuery event data.
Whenever GA4 encounters an event without a previous session, it can consider it as a part of a new session.
That means if GA4 detects an event and no existing session is active (based on ‘ga_session_id’ or user session logic), it can create a new session regardless of the event type.
In other words, any GA4 event can potentially start a new session if no active session exists.
For example:
#1 If the first event of a user’s visit is a custom event (like a click or custom engagement event) or ecommerce event (like ‘purchase’), and neither ‘session_start’ nor ‘user_engagement’ has fired, GA4 can infer the start of a session from this first event. This behaviour is common with server-side tracking or Measurement Protocol implementations.
#2 A session can start with ‘page_view’ event due to delayed or missing ‘session_start’ event. In this case, GA4 assumes the session started with the ‘page_view’.
#3 A session can start with a ‘page_view’ event due to users leaving your website before the 10-second user engagement threshold.
#4 A session can start without ‘session_start’ event or the ‘user_engagement’ event if server-side events are sent via Measurement Protocol.
When Measurement Protocol is used, GA4 tracks only what it is explicitly told to track.
If the Measurement Protocol request does not include a ‘session_start’ event, GA4 will infer the session from the first event.
Since Measurement Protocol does not automatically generate ‘user_engagement’, this event will also be absent.
Whatever is the first event will mark the start of the session.
A session can start without ‘session_start’ event or the ‘user_engagement’ event.
A session can start without ‘session_start’ event or the ‘user_engagement’ event if ‘ga_session_id’ is missing from the Measurement Protocol payload.
This is a common misconfiguration when using server-side tracking or custom event tracking via the Measurement Protocol.
Server-side tracking could fail to maintain session continuity, creating a new session for each hit.
If server-side tracking does not maintain the same ga_session_id for multiple hits (like purchase, add_to_cart, etc.), GA4 will assume each event belongs to a new session.
Therefore the most accurate method to calculate ‘sessions’ in GA4 Bigquery is to count each unique combination of ‘user_pseudo_id’ and ‘ga_session_id’.
This method accounts for all sessions, regardless of whether they start with ‘session_start’ and/or ‘user_engagement’ events.
