Skip to content

fix(replay): gate event-trigger starts behind the replay checks - #758

Open
posthog[bot] wants to merge 2 commits into
mainfrom
posthog-self-driving/fixreplay-gate-android-event-trigger-3831bf
Open

fix(replay): gate event-trigger starts behind the replay checks#758
posthog[bot] wants to merge 2 commits into
mainfrom
posthog-self-driving/fixreplay-gate-android-event-trigger-3831bf

Conversation

@posthog

@posthog posthog Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

Android's session replay event-trigger path called the recording transition directly. A matching event could therefore start replay even when the local replay switch was off, the project or linked feature flag rejected the session, or sampling excluded it. That automatic start was then classified as manual and could survive later gate checks.

This change makes event triggers satisfy only the event gate. Every other applicable replay gate must also pass before automatic recording starts.

Related report: session replay event trigger bypass

What changed

  • Record event-trigger activation before evaluating the remaining replay gates, so another gate can open later in the same session without requiring the event again.
  • Keep explicit start intent separate from automatic recording transitions.
  • Preserve an explicit manual start while it waits for an event trigger.
  • Let stopSessionReplay() cancel that pending request even while replay is inactive.
  • Re-check replay permission before a queued remote-config resume runs.

No public API or generated API surface changes are required.

💚 How did you test it?

Ran locally on JDK 17:

  • Focused PostHogTest and PostHogReplayIntegrationTest suites
  • make test
  • make checkFormat
  • ./gradlew apiCheck
  • git diff --check

Coverage includes the linked-flag/event 2×2 matrix, local enablement, sampling rejection, retained trigger activation, session rotation, deferred manual starts, explicit cancellation while inactive, and automatic/manual provenance under a configuration race.

A fresh SDK code review of the final diff found no blocking, important, or minor issues.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm changeset to generate a changeset file

@dustinbyrne dustinbyrne self-assigned this Sep 4, 2026
A matching event trigger called start() directly, so it skipped the master
switch, the project flag and the sampling decision that every other start
path applies. An app that gates replay behind its own feature flag kept
recording the users the flag excludes. start() then set
startedWithAutomaticDisabled, so the recording counted as manually started
and survived every later check.

onEvent now reuses isRecordingPermittedForCurrentSession() before it starts,
after it records the trigger activation, so the trigger only lifts the
event-trigger gate.

start() remembers an explicit start asked for while automatic replay is off.
The trigger gate defers that start, so without this the manual intent is lost
and the deferred recording is refused once the trigger matches.

Generated-By: PostHog Desktop
Task-Id: d4a23d7a-bccc-4e65-9061-9223a7937811
@dustinbyrne
dustinbyrne force-pushed the posthog-self-driving/fixreplay-gate-android-event-trigger-3831bf branch from 2be3a21 to 1ba2a1f Compare September 4, 2026 16:23
@dustinbyrne
dustinbyrne changed the base branch from posthog-self-driving/fixreplay-stop-android-replay-when-its-fea916 to main September 4, 2026 16:24
@dustinbyrne
dustinbyrne force-pushed the posthog-self-driving/fixreplay-gate-android-event-trigger-3831bf branch from 1ba2a1f to 3ad26f1 Compare September 4, 2026 16:27
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

posthog-android Compliance Report

Date: 2026-09-04 16:34:17 UTC
Duration: 118787ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 414ms
Format Validation.Event Has Uuid 35ms
Format Validation.Event Has Lib Properties 31ms
Format Validation.Distinct Id Is String 36ms
Format Validation.Token Is Present 30ms
Format Validation.Custom Properties Preserved 27ms
Format Validation.Event Has Timestamp 26ms
Retry Behavior.Retries On 503 7038ms
Retry Behavior.Does Not Retry On 400 4030ms
Retry Behavior.Does Not Retry On 401 4029ms
Retry Behavior.Respects Retry After Header 7032ms
Retry Behavior.Implements Backoff 17032ms
Retry Behavior.Retries On 500 7024ms
Retry Behavior.Retries On 502 7022ms
Retry Behavior.Retries On 504 7026ms
Retry Behavior.Max Retries Respected 17025ms
Deduplication.Generates Unique Uuids 51ms
Deduplication.Preserves Uuid On Retry 7020ms
Deduplication.Preserves Uuid And Timestamp On Retry 12037ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 7027ms
Deduplication.No Duplicate Events In Batch 55ms
Deduplication.Different Events Have Different Uuids 40ms
Compression.Sends Gzip When Enabled 29ms
Batch Format.Uses Proper Batch Structure 31ms
Batch Format.Flush With No Events Sends Nothing 18ms
Batch Format.Multiple Events Batched Together 46ms
Error Handling.Does Not Retry On 403 4032ms
Error Handling.Does Not Retry On 413 4026ms
Error Handling.Retries On 408 5036ms

Feature_Flags Tests

17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 46ms
Request Payload.Flags Request Uses V2 Query Param 35ms
Request Payload.Flags Request Hits Flags Path Not Decide 25ms
Request Payload.Flags Request Omits Authorization Header 35ms
Request Payload.Token In Flags Body Matches Init 28ms
Request Payload.Groups Round Trip 32ms
Request Payload.Groups Default To Empty Object 26ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 30ms
Request Payload.Disable Geoip Omitted Defaults To False 33ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 25ms
Request Lifecycle.No Flags Request On Init Alone 14ms
Request Lifecycle.No Flags Request On Normal Capture 35ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 47ms
Request Lifecycle.Mock Response Value Is Returned To Caller 35ms
Retry Behavior.Retries Flags On 502 331ms
Retry Behavior.Retries Flags On 504 333ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 30ms

@dustinbyrne
dustinbyrne marked this pull request as ready for review September 4, 2026 17:17
@dustinbyrne
dustinbyrne requested a review from a team as a code owner September 4, 2026 17:17
@dustinbyrne

Copy link
Copy Markdown
Contributor

Android device smoke test

Tested head 3ad26f1b with the sample app on an Android 16 / API 36 emulator. A controlled local PostHog server exercised the SDK's real /config, /flags, /batch, and /s/ network paths and recorded whether replay payloads were emitted.

All scenarios passed:

  • replay without ingestion controls still starts automatically and emits /s/ payloads
  • a non-matching event does not start replay
  • a matching event starts replay when the local switch, project setting, linked flag, and sampling gates pass
  • linked flag false, sample rate 0.0, project disablement, and local disablement each prevent replay payloads
  • trigger activation resets after session rotation
  • manual start with the local automatic switch off remains inactive before the trigger and after a non-matching event, then starts and emits replay only after the configured event matches
  • stopping while that manual start is pending cancels it; a later matching event remains inactive and emits no replay

As a sensitivity check, the same runtime probe against merge base 0d7f5670 reproduced the original defect: with the linked flag false, a matching event activated replay and emitted /s/ payloads. The PR head correctly blocks that path.

No crashes or ANRs occurred. This was a debug-build emulator smoke test against controlled server responses; it did not modify or depend on a PostHog Cloud project's configuration.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant