Skip to content

fix(analytics): send crashes via captureException so they reach Error Tracking#3

Merged
willwade merged 4 commits into
mainfrom
fix/crash-reporting-capture-exception
Jun 29, 2026
Merged

fix(analytics): send crashes via captureException so they reach Error Tracking#3
willwade merged 4 commits into
mainfrom
fix/crash-reporting-capture-exception

Conversation

@willwade

Copy link
Copy Markdown
Contributor

Fixes #2.

Problem

PostHog Error Tracking showed No Exception events have been detected! because we sent custom crash events via PostHog.capture(\"crash\", ...). Error Tracking only surfaces events sent by PostHog.captureException (which produce \ events).

Changes

  • installCrashHandler — if the user has opted in (PostHog initialised), call PostHog.captureException(throwable, props) live with engine_log_tail / locale / thread as properties, then chain to the previous handler. The crash file is now written only for the deferred (not-yet-opted-in) case.
  • flushPendingCrash — rebuilds a synthetic Throwable from the saved stack trace (parseStackTrace) and sends via captureException, so deferred crashes also land in Error Tracking.
  • PostHog debug = BuildConfig.DEBUG so the capture → flush → HTTP-send path is visible in logcat during end-to-end verification.
  • Debug Diagnostics UI (debug builds only, Settings → Privacy): Send test exception (non-destructive captureException) and Crash app (real throw on a worker thread to exercise the full handler). RFC 0009.
  • First JVM unit tests (RFC 0011): parseStackTrace + scrub.

A bug the tests caught

The scrub tests revealed that the Windows home-path scrubbing was already broken in the merged crash code — it used the string overload of Regex.replace, where backslashes in the replacement are treated as escape characters, so C:\Users\bob\ was mis-emitted and PII leaked. Switched to the lambda overload; all 5 tests pass.

No SDK bump

posthog-android:3.51.0 already pulls in posthog core 6.21.0, which exposes captureException. Confirmed against the resolved jar — no dependency change needed.

Verification done

  • compileDebugKotlin ✓, assembleDebug
  • testDebugUnitTest ✓ (5/5, incl. the regression test for the scrub bug)
  • Install/launch smoke test blocked by a transient wedged package service on the emulator (not a code issue).

Manual end-to-end check (needs your dashboard eyes)

  1. Run a debug build, opt in to analytics.
  2. Settings → Privacy → Diagnostics → Send test exception.
  3. Confirm a \ event appears in PostHog → Error Tracking (logcat will show the SDK sending it, thanks to debug=true).
  4. (Optional) Crash app exercises the real uncaught-handler path; relaunch and the live captureException should fire (or, if not opted in, the deferred file flushes next launch).

Native SIGSEGV in libdasher.so remains out of scope (signal-shim follow-up, per RFC 0009).

DCO: commit is Signed-off-by.

willwade added 4 commits June 28, 2026 22:23
… Tracking

Fixes #2. PostHog Error Tracking only surfaces events sent by
PostHog.captureException (which produce $exception events); our previous
capture("crash", ...) calls were invisible to it (showed in Events only).

- installCrashHandler: if opted in, call PostHog.captureException(throwable,
  props) live (with engine_log_tail + locale + thread), then chain. The crash
  file is now written ONLY for the deferred (not-yet-opted-in) case.
- flushPendingCrash: rebuild a synthetic Throwable from the saved stack trace
  (parseStackTrace) and send via captureException so deferred crashes also land
  in Error Tracking.
- PostHog debug=true on debug builds so the capture/flush/HTTP-send path is
  visible in logcat during end-to-end verification.
- Debug Diagnostics UI (debug builds only, Settings > Privacy): 'Send test
  exception' (non-destructive captureException) and 'Crash app' (real throw on
  a worker thread to exercise the full handler). RFC 0009.
- First JVM unit tests (RFC 0011): parseStackTrace + scrub. The scrub tests
  caught a real bug - the Windows home-path replacement used the string overload
  of Regex.replace where backslashes are escape chars, so the C:\Users\ path was
  mis-emitted and PII leaked. Switched to the lambda overload; all 5 tests pass.

No SDK bump needed: posthog-android 3.51.0 already pulls in posthog core
6.21.0, which exposes captureException.

Signed-off-by: will wade <willwade@gmail.com>
…r script

Per review, the in-app "Send test exception" / "Crash app" diagnostics buttons
don't belong in the shipped app. Removed both UI buttons (SettingsScreen) and
the AnalyticsService.sendTestException / triggerRealCrash methods that backed
them. The captureException fix, the scrub-bug fix, the unit tests, and the
PostHog debug=true logging all remain.

Adds scripts/verify_posthog_exceptions.py: a one-off developer tool that sends
a single synthetic $exception to the same PostHog project via the official
Python SDK (posthog.capture_exception), so the project's Error Tracking can be
verified independently of the app/emulator. Tagged test=true for filtering.

Signed-off-by: will wade <willwade@gmail.com>
The verifier script was missing the trailing 'J' on the PostHog project key,
so its test events were silently dropped by PostHog (the SDK returns a
client-generated event id before the server validates the key, which is why
it looked like it sent). The app's AnalyticsService TOKEN was already correct.
Fixes the verification path.

Signed-off-by: will wade <willwade@gmail.com>
Per review - the standalone Python verifier was useful for one-off setup
confirmation but doesn't belong in the app repo. Removes scripts/verify_posthog_exceptions.py.

Signed-off-by: will wade <willwade@gmail.com>
@willwade willwade merged commit 51414f4 into main Jun 29, 2026
2 checks passed
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.

PostHog Error Tracking: use captureException() instead of custom crash events

1 participant