(feat): richer Sentry exceptions in span — chained causes, mechanism, typed constants#88
Merged
Merged
Conversation
…n span Sentry exporter - Walk the error's getPrevious() chain (capped at 10) and send every link as a separate exception value, root cause first, so Sentry renders the full cause tree instead of only the outermost exception - Attach mechanism to each value: type/handled (fatal = unhandled), and exception_id/parent_id/source links for chained values - Name the throw-site frame's enclosing function (was sent anonymous) - Include the exception's namespace as module per value - Type all class constants (PHP 8.3) and bump the PHP floor to >=8.3 — Storage\Coroutine already used a typed constant, so 8.2 was never actually supported
Contributor
Greptile SummaryAdds richer Sentry exception payloads and aligns the span package with PHP 8.3.
Confidence Score: 5/5The PR appears safe to merge. No blocking failures remain within the scope of the previous review thread. Important Files Changed
Reviews (3): Last reviewed commit: "(fix): honor explicit span.handled attri..." | Re-trigger Greptile |
Member
Author
|
@greptile review |
…entry exporter - mechanism.handled now prefers a boolean span.handled attribute; the level-based heuristic (fatal = unhandled) is only the fallback - collapse empty test exception class body per pint single_line_empty_body
loks0n
approved these changes
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes span's Sentry exporter emit the exception structure Sentry's UI is built around — matching what mature SDKs (e.g. sentry-python) send — so events show the full cause chain, handled/unhandled chips, and named frames instead of a single flat exception. Also types every class constant and fixes the package's PHP floor.
Why
Comparing a span-reported event against a sentry-python one, span events were missing:
getPrevious()was ignored, so the root cause (e.g. the underlying S3/curl error behind a storage failure) was silently droppedhandled: true/falsechip, no cause-tree linkingChanges
1. Chained exceptions (Sentry exception interface)
Before — only the outermost exception, previous causes dropped:
After — the full
getPrevious()chain (capped at 10), root cause first, each value linked into a mechanism tree:handledderives from the exported level: warning/error spans were caught and reported by app code (handled: true); fatal spans came from a process-level handler (handled: false).2. Throw-site frame gets its enclosing function
PHP traces describe call sites, so the exporter appends a synthetic frame for the throw site — but it was sent without a
function, rendering an anonymous top frame in Sentry.Before:
After — named from the callee recorded in the first trace frame:
Each exception value also gains
module(the exception's namespace) when it has one.3. Typed constants + honest PHP floor
Before / after:
Note:
Storage\Coroutinealready usedprivate const string(a PHP 8.3 feature), so the>=8.2constraint was already broken — 8.2 would fatal on parse. This makes the floor honest, but it is a platform-requirement bump worth flagging in the next release.Testing
New envelope-level tests in
SentryTest.php(decode the actual envelope payload) covering: chain ordering (root cause first),exception_id/parent_id/sourcelinking,handledtrue for error / false for fatal,modulepresence for namespaced exceptions and absence for global ones, function names on all frames including the throw site, and the 10-deep chain cap keeping the newest exceptions.bin/monorepo test span: 105 tests, 218 assertions, all passing.bin/monorepo validate: all packages valid.