Skip to content

fix(android): report a missing runtime dependency instead of staying silent - #803

Merged
ioannisj merged 3 commits into
mainfrom
posthog-self-driving/fixandroid-report-when-a-missing-5557a4
Oct 17, 2026
Merged

ioannisj merged 3 commits into
mainfrom
posthog-self-driving/fixandroid-report-when-a-missing-5557a4

Conversation

@posthog

@posthog posthog Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

  • A customer lost every Android session recording for two to three months. Their build excluded com.squareup.curtains:curtains, and the SDK never told them. iOS kept recording, so the search went to Android config rather than to a missing class.
  • curtains is a hard implementation dependency that three integrations need. When it is absent the SDK has two bad outcomes, neither of which names the artifact:
Where it breaks Before After
Integration is built (PostHogAndroid.setAndroidConfig) NoClassDefFoundError leaves setup() uncaught Warning names the artifact; the other integrations still install
Integration is installed (PostHog.setup loop) Debug-only log, sessionReplayHandler never assigned Warning names the artifact and what stays off
  • The swallow was never specific to curtains: any integration that fails disappears the same way.

💚 How did you test it?

  • make test and make checkFormat pass. ./gradlew apiCheck passes against the refreshed dump.
  • New tests: the core install loop (PostHogIntegrationInstallFailureTest), the Android construction guard (PostHogAndroidIntegrationFailureTest), and the Logcat severity (PostHogAndroidLoggerTest — a warning reaches Logcat with debug = false, a debug message does not).
  • Reproduced the real failure, not only an injected one. I removed the curtains jars from the unit-test runtime classpath and ran PostHogAndroid.setup() under Robolectric.
Reproduction on a runtime without curtains

Before, setup() does not return:

OUTCOME: setup threw java.lang.NoClassDefFoundError: curtains/OnRootViewsChangedListener

After, setup() completes and says why:

WARN: Integration PostHogReplayIntegration failed to install: java.lang.NoClassDefFoundError: curtains/OnRootViewsChangedListener. Your build excludes the 'com.squareup.curtains:curtains' artifact, which the PostHog SDK needs. Restore that artifact. Until you do, this integration and every feature that it provides stay off for the whole process.
WARN: Integration PostHogTouchActivityIntegration failed to install: java.lang.NoClassDefFoundError: curtains/TouchEventInterceptor. ...
SETUP RETURNED, integrations=[PostHogLogCatIntegration, PostHogActivityLifecycleCallbackIntegration, PostHogAppInstallIntegration, PostHogLifecycleObserverIntegration, PostHogPushSubscriptionIntegration, PostHogSendCachedEventsIntegration, PostHogErrorTrackingAutoCaptureIntegration]

The classpath filter and the probe test were scaffolding and are not in the diff.

  • Not tested on a device or an emulator. No emulator was available in this environment.

Note

PostHogLogger.logWarning is a defaulted interface method that delegates to log, so a custom logger keeps its current behaviour and stays binary compatible. It is deliberately not named warn, to keep it distinct from the user-facing com.posthog.logs.PostHogLogger.warn, which sends log records to PostHog.

📝 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

🤖 Agent context

Autonomy: Fully autonomous

  • Built with Claude Opus 5 in PostHog Desktop, from the linked inbox report.
  • The report located the defect in the catch (e: Throwable) of the install loop. That is real and is fixed, but a review pass found that it is not where an absent curtains first lands: all three integrations build their curtains listeners in their constructors, so the class resolves during PostHogAndroid.setAndroidConfig, before PostHog.setup(). I confirmed this in the bytecode and then in the reproduction above. Fixing only the install loop would have left the advertised warning unreachable, so the construction site is guarded too.
  • Rejected along the way: making the listener fields lazy to push the failure into install() (larger, riskier, three classes); declaring the required artifact on PostHogIntegration (widens a public interface that CONTRIBUTING.md gates behind prior agreement); reusing the logs-product logger for a setup-time diagnostic (circular, and it would ship the diagnostic as customer log data).
  • addIntegrationSafely is applied to every integration rather than only the curtains-backed three, so construction follows the same policy the install loop already has.

Created with PostHog Desktop from this inbox report.

Origin

  • Conversations: ticket
  • First signal: 2026-09-15
  • Inbox report: open
  • Likely cause: 1e14579
  • Task started by: auto-start, after the report was rated P2 and ready to fix

…silent

A host build that excludes com.squareup.curtains:curtains breaks session
replay, interaction autocapture and touch tracking. The SDK gave the app no
usable signal: an install failure went to a debug-only log, and a build error
before install() propagated out of PostHogAndroid.setup().

- PostHogLogger gains logWarning, which prints even when debug is off.
- A missing class at install time is now reported with the Maven coordinate of
  the artifact the build excludes, and what stays off.
- PostHogAndroid builds each integration through addIntegrationSafely, so one
  integration that cannot be built reports itself and the rest still install.
- README states that the curtains artifact must not be excluded.

Generated-By: PostHog Desktop
Task-Id: c3811e92-1c7f-46c7-a81c-e139056782bb
@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

posthog-android Compliance Report

Date: 2026-10-17 01:34:01 UTC
Duration: 118473ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

✅ 29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 392ms
Format Validation.Event Has Uuid ✅ 27ms
Format Validation.Event Has Lib Properties ✅ 29ms
Format Validation.Distinct Id Is String ✅ 25ms
Format Validation.Token Is Present ✅ 26ms
Format Validation.Custom Properties Preserved ✅ 28ms
Format Validation.Event Has Timestamp ✅ 28ms
Retry Behavior.Retries On 503 ✅ 7029ms
Retry Behavior.Does Not Retry On 400 ✅ 4026ms
Retry Behavior.Does Not Retry On 401 ✅ 4025ms
Retry Behavior.Respects Retry After Header ✅ 7026ms
Retry Behavior.Implements Backoff ✅ 17037ms
Retry Behavior.Retries On 500 ✅ 7020ms
Retry Behavior.Retries On 502 ✅ 7019ms
Retry Behavior.Retries On 504 ✅ 7016ms
Retry Behavior.Max Retries Respected ✅ 17035ms
Deduplication.Generates Unique Uuids ✅ 39ms
Deduplication.Preserves Uuid On Retry ✅ 7017ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 12030ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 7020ms
Deduplication.No Duplicate Events In Batch ✅ 36ms
Deduplication.Different Events Have Different Uuids ✅ 24ms
Compression.Sends Gzip When Enabled ✅ 18ms
Batch Format.Uses Proper Batch Structure ✅ 20ms
Batch Format.Flush With No Events Sends Nothing ✅ 15ms
Batch Format.Multiple Events Batched Together ✅ 37ms
Error Handling.Does Not Retry On 403 ✅ 4020ms
Error Handling.Does Not Retry On 413 ✅ 4022ms
Error Handling.Retries On 408 ✅ 5028ms

Feature_Flags Tests

✅ 17/17 tests passed

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

@github-actions

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, post a comment or remove the stale label – otherwise this will be closed in another week.

@github-actions github-actions Bot added the stale label Sep 29, 2026
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

This PR was closed due to lack of activity. Feel free to reopen if it's still relevant.

@github-actions github-actions Bot closed this Oct 7, 2026
@ioannisj ioannisj reopened this Oct 8, 2026
@github-actions github-actions Bot removed the stale label Oct 9, 2026
@ioannisj
ioannisj marked this pull request as ready for review October 12, 2026 18:13
@ioannisj
ioannisj requested a review from a team as a code owner October 12, 2026 18:13
Comment thread posthog/src/main/java/com/posthog/internal/PostHogUtils.kt Outdated
@ioannisj
ioannisj merged commit c5e788b into main Oct 17, 2026
16 checks passed
@ioannisj
ioannisj deleted the posthog-self-driving/fixandroid-report-when-a-missing-5557a4 branch October 17, 2026 01:52
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.

2 participants