Version
3.48.3
Steps to Reproduce
- Integrate the PostHog iOS SDK with error tracking auto-capture enabled in the dashboard.
- Install the app on a device or simulator for the first time (no prior disk-cached remote config).
- Trigger a crash or unhandled exception on first launch.
- Relaunch the app, observe that the crash is not reported in PostHog Error Tracking.
- Trigger the same crash on the second launch, observe that it is reported this time.
Expected Result
The error tracking auto-capture integration installs on the first launch, even if the remote config network fetch has not yet completed, by subscribing to remote config updates and performing a late install, matching the behaviour of the session replay integration.
Actual Result
The error tracking integration is installed synchronously during setup(), before the async remote config fetch completes. On first launch, no disk-cached config exists, so isAutocaptureExceptionsEnabled() returns false and the integration throws. When the fetch eventually completes, nothing re-installs the integration. Any crash on first launch is permanently lost.
Code References (courtesy of claude code)
-
PostHogSDK.setup() calls installIntegrations() synchronously (PostHogSDK.swift#L157)
-
installIntegrations() iterates integrations and calls install() (PostHogSDK.swift#L1898-L1933) — if install() throws, the integration is skipped and never retried.
-
Error tracking install() gates on remote config (PostHogErrorTrackingAutoCaptureIntegration.swift#L22-L25):
if postHog.remoteConfig?.isAutocaptureExceptionsEnabled() == false {
throw InternalPostHogError(description: "Error tracking auto capture integration disabled in remote config.")
}
At this point, only the disk-cached config has been loaded via preloadErrorTrackingConfig() (PostHogRemoteConfig.swift#L489-L496).
-
Remote config fetch is async — preloadRemoteConfig() dispatches to dispatchQueue.async (PostHogRemoteConfig.swift#L105), so it completes after installIntegrations() has already run and failed.
-
processErrorTrackingConfig() updates the flag but doesn't re-install (PostHogRemoteConfig.swift#L467-L487) — it sets autoCaptureExceptions and caches to disk, but there's no callback to PostHogSDK to retry the integration.
-
onRemoteConfigLoaded is only subscribed to by session replay (PostHogReplayIntegration.swift#L153), not by error tracking. Session replay has installReplayIntegration() (PostHogSDK.swift#L1939-L1952) as a late-install path — error tracking has no equivalent.
-
Even if installIntegrations() were called again, the guard at the top would block it (PostHogSDK.swift#L1899):
guard installedIntegrations.isEmpty else {
hedgeLog("Integrations already installed.")
return
}
Other integrations (lifecycle, replay, surveys) installed successfully, so the array is non-empty.
Suggested Fix
Add an installErrorTrackingIntegration() method (analogous to installReplayIntegration()) and subscribe to onRemoteConfigLoaded in PostHogSDK to call it when the config flips to enabled.
Version
3.48.3
Steps to Reproduce
Expected Result
The error tracking auto-capture integration installs on the first launch, even if the remote config network fetch has not yet completed, by subscribing to remote config updates and performing a late install, matching the behaviour of the session replay integration.
Actual Result
The error tracking integration is installed synchronously during
setup(), before the async remote config fetch completes. On first launch, no disk-cached config exists, soisAutocaptureExceptionsEnabled()returnsfalseand the integration throws. When the fetch eventually completes, nothing re-installs the integration. Any crash on first launch is permanently lost.Code References (courtesy of claude code)
PostHogSDK.setup()callsinstallIntegrations()synchronously (PostHogSDK.swift#L157)installIntegrations()iterates integrations and callsinstall()(PostHogSDK.swift#L1898-L1933) — ifinstall()throws, the integration is skipped and never retried.Error tracking
install()gates on remote config (PostHogErrorTrackingAutoCaptureIntegration.swift#L22-L25):At this point, only the disk-cached config has been loaded via
preloadErrorTrackingConfig()(PostHogRemoteConfig.swift#L489-L496).Remote config fetch is async —
preloadRemoteConfig()dispatches todispatchQueue.async(PostHogRemoteConfig.swift#L105), so it completes afterinstallIntegrations()has already run and failed.processErrorTrackingConfig()updates the flag but doesn't re-install (PostHogRemoteConfig.swift#L467-L487) — it setsautoCaptureExceptionsand caches to disk, but there's no callback toPostHogSDKto retry the integration.onRemoteConfigLoadedis only subscribed to by session replay (PostHogReplayIntegration.swift#L153), not by error tracking. Session replay hasinstallReplayIntegration()(PostHogSDK.swift#L1939-L1952) as a late-install path — error tracking has no equivalent.Even if
installIntegrations()were called again, the guard at the top would block it (PostHogSDK.swift#L1899):Other integrations (lifecycle, replay, surveys) installed successfully, so the array is non-empty.
Suggested Fix
Add an
installErrorTrackingIntegration()method (analogous toinstallReplayIntegration()) and subscribe toonRemoteConfigLoadedinPostHogSDKto call it when the config flips to enabled.