Skip to content

feat(error-tracking): emit stack frames in canonical bottom-up order - #160

Merged
cat-ph merged 1 commit into
mainfrom
cat/canonical-frame-order
Jul 23, 2026
Merged

feat(error-tracking): emit stack frames in canonical bottom-up order#160
cat-ph merged 1 commit into
mainfrom
cat/canonical-frame-order

Conversation

@cat-ph

@cat-ph cat-ph commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Problem

PostHog's error tracking expects a single canonical stack-frame order across every SDK so the ingestion pipeline can fingerprint, group, and display exceptions consistently. The canonical wire order for $exception_list[].stacktrace.frames is bottom-up: frames[0] is the outermost entry point and the last frame is the crash site.

The Elixir SDK's logger handler built frames directly from __STACKTRACE__, which is innermost-first (crash site first). This is the reverse of the canonical order, so Elixir exceptions were emitted upside-down relative to the other SDKs.

Part of cross-SDK stack-frame-order standardization: PostHog/sdk-specs#11

Change

  • In lib/posthog/handler.ex, do_stacktrace/3 now reverses the frame list it builds from __STACKTRACE__, so the emitted frames are bottom-up (entry point first, crash site last).
  • Arity-based frame formatting inside do_stacktrace/3 is untouched; only list order flips.
  • Source-context fields (context_line / pre_context / post_context) stay attached to their own frames; only the order of the list changes.
  • $exception_list ordering is unchanged (already canonical: [0] = outermost/reported exception).
  • The synthetic single-frame logger path (the stacktrace/3 clause that emits one synthetic: true frame) is unaffected: a single-frame list is trivially canonical, and nothing in that path assumes crash-first order.

Tests

  • Updated all order-dependent assertions in test/posthog/handler_test.exs to expect bottom-up frames. Head-matches that pinned the crash site as the first frame were converted to tail assertions (List.last/1 / Enum.take(frames, -3)).
  • Added an explicit regression test: the first frame is the entry point (Task.Supervised.invoke_mfa/2) and the last frame is the crash site (anonymous fn/0 in LoggerHandlerKit.Act.task_error/1).
  • mix test: 308 passed, 18 excluded. mix format --check-formatted: clean.

Coordination

This is a breaking wire-order change for the frames array. It should be merged/released only after the pipeline normalization gate (cymbal) is live, so that exceptions ingested during the transition are normalized to a single canonical order regardless of which SDK version produced them.

Released as a MINOR version bump so the pipeline can gate normalization on $lib_version.

Note: the pipeline currently has no dedicated Elixir stack handler; that gap is tracked in the spec PR above.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Reviews (1): Last reviewed commit: "feat(error-tracking): emit stack frames ..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

posthog-elixir Compliance Report

Date: 2026-07-23 18:03:54 UTC
Duration: 117477ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 610ms
Format Validation.Event Has Uuid 608ms
Format Validation.Event Has Lib Properties 608ms
Format Validation.Distinct Id Is String 608ms
Format Validation.Token Is Present 608ms
Format Validation.Custom Properties Preserved 608ms
Format Validation.Event Has Timestamp 608ms
Retry Behavior.Retries On 503 5613ms
Retry Behavior.Does Not Retry On 400 2610ms
Retry Behavior.Does Not Retry On 401 2611ms
Retry Behavior.Respects Retry After Header 5614ms
Retry Behavior.Implements Backoff 15617ms
Retry Behavior.Retries On 500 5614ms
Retry Behavior.Retries On 502 5613ms
Retry Behavior.Retries On 504 5614ms
Retry Behavior.Max Retries Respected 15624ms
Deduplication.Generates Unique Uuids 618ms
Deduplication.Preserves Uuid On Retry 5613ms
Deduplication.Preserves Uuid And Timestamp On Retry 10609ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 5615ms
Deduplication.No Duplicate Events In Batch 613ms
Deduplication.Different Events Have Different Uuids 610ms
Compression.Sends Gzip When Enabled 608ms
Batch Format.Uses Proper Batch Structure 609ms
Batch Format.Flush With No Events Sends Nothing 606ms
Batch Format.Multiple Events Batched Together 612ms
Error Handling.Does Not Retry On 403 2607ms
Error Handling.Does Not Retry On 413 2611ms
Error Handling.Retries On 408 5614ms

Feature_Flags Tests

17/17 tests passed

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

@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 Jul 10, 2026
@github-actions

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 Jul 20, 2026
@cat-ph cat-ph reopened this Jul 23, 2026
Reverse the stacktrace frames emitted by the logger handler so that
`$exception_list[].stacktrace.frames` follows the canonical cross-SDK
wire order: frames[0] is the outermost entry point and the last frame
is the crash site. Aligns the Elixir SDK with the cross-SDK stack frame
ordering standard.
@cat-ph
cat-ph force-pushed the cat/canonical-frame-order branch from 1e50ef5 to 5b5dc63 Compare July 23, 2026 18:00
@cat-ph
cat-ph marked this pull request as ready for review July 23, 2026 18:05
@cat-ph
cat-ph requested a review from a team as a code owner July 23, 2026 18:05
@cat-ph
cat-ph requested review from a team, ablaszkiewicz and hpouillot July 23, 2026 18:05
@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown

Reviews (2): Last reviewed commit: "feat(error-tracking): emit stack frames ..." | Re-trigger Greptile

@cat-ph
cat-ph merged commit d66f174 into main Jul 23, 2026
33 checks passed
@cat-ph
cat-ph deleted the cat/canonical-frame-order branch July 23, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants