Skip to content

Bug: error tracking autocapture misses crashes on first launch before remote config resolves #648

Description

@ioannisj

Version

3.55.2 (also reproduces on current main)

Steps to Reproduce

  1. Integrate posthog-android with error tracking autocapture enabled locally (config.errorTrackingConfig.autoCapture = true) and enabled in the project settings (remote autocaptureExceptions).
  2. Install the app for the first time (no prior disk-cached remote config).
  3. Trigger an uncaught exception very early on first launch, before the remote config (/flags) response arrives.
  4. Relaunch and observe the crash is not reported in PostHog Error Tracking.
  5. Trigger the same crash on a later launch (cache now present) and observe it is reported.

Expected Result

On first launch, the UncaughtExceptionHandler should install by default when local errorTrackingConfig.autoCapture is on and nothing in the cached or loaded remote config explicitly disables it. That way a crash early on the first launch, before the remote config fetch finishes, still gets captured. If the config then comes back with autocaptureExceptions: false, uninstall the handler. Same shape as the iOS fix in PostHog/posthog-ios#731 (issue PostHog/posthog-ios#551).

Actual Result

The integration installs synchronously during setup(), before the async remote config fetch finishes. install() bails unless the remote config already says autocapture is enabled. On a first launch there is no disk cache, so isAutocaptureExceptionsEnabled() is false and install() returns without registering the handler. Any uncaught exception between app start and the remote config response is lost.

This is not a permanent miss like the iOS bug (PostHog/posthog-ios#551). Once the config resolves in the same session, onRemoteConfig(loaded = true) retries the install and the handler comes up, so later crashes in that session are caught. The gap is only the first-launch window before the first config response.

A couple of related cases:

  • if the first-launch config fetch fails, onRemoteConfig(loaded = false) returns early, so autocapture never installs for that whole session (offline first launch).
  • the same window applies the first time autocapture gets turned on server-side, until the config lands and is cached.

Code References

Pinned to 92fa733.

  1. Integration registered, then install() called synchronously during setup. PostHog.kt#L256 registers it; PostHog.kt#L282 runs the install loop.

  2. install() bails unless remote config already reports enabled. PostHogErrorTrackingAutoCaptureIntegration.kt#L38-L40:

    val autocaptureExceptionsEnabled = config.remoteConfigHolder?.isAutocaptureExceptionsEnabled() ?: false
    if (!autocaptureExceptionsEnabled) {
        return
    }
  3. Only the disk cache is available at that point. preloadErrorTrackingConfig() reads the cached value in the PostHogRemoteConfig init block (PostHogRemoteConfig.kt#L552); isAutocaptureExceptionsEnabled() is autoCaptureExceptions && errorTrackingConfig.autoCapture (PostHogRemoteConfig.kt#L635). On first launch there is no cache, so it is false.

  4. The remote config fetch is async, fired after the install loop. PostHog.kt#L321 loadRemoteConfigRequest(...) runs on remoteConfigExecutor after install() has already returned.

  5. Late install works, but only after the fetch. onRemoteConfigLoaded calls notifyIntegrationsRemoteConfig(config, loaded = true/false) (PostHog.kt#L201-L203), which iterates integrations calling onRemoteConfig(loaded) (PostHog.kt#L342-L346); the integration then installs iff enabled (PostHogErrorTrackingAutoCaptureIntegration.kt#L71). A loaded = false (failed fetch) is a no-op.

Suggested Fix

Mirror the iOS approach in PostHog/posthog-ios#731: in install(), install the handler by default when local errorTrackingConfig.autoCapture is on and there is no cached-or-loaded remote config that explicitly disables it, instead of waiting for an affirmative enable. Keep onRemoteConfig(loaded = true) to uninstall() if the resolved config comes back with autocaptureExceptions: false. That catches the first-launch pre-config crashes while still honoring the remote kill-switch, with local config as the primary gate.

This reaches Flutter too: the Flutter Android plugin maps captureNativeExceptions onto errorTrackingConfig.autoCapture, so a Flutter-on-Android app has the same first-launch gap.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions