Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .sampo/changesets/demangler-robust-frame-strip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
cargo/posthog-rs: patch
---

fix: strip SDK capture frames under newer demangler renderings (`<Type>::method::<T>`), which previously survived at the crash-site end of the stack
23 changes: 23 additions & 0 deletions src/error_tracking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,11 @@ fn is_panic_dispatcher_frame(function: &str) -> bool {
// the strip loop stops at them and they survive, classified out-of-app for the
// UI to collapse.
fn is_internal_capture_frame(function: &str) -> bool {
// Demanglers differ on qualified-path rendering across toolchain versions:
// older output is `Exception::from_error`, newer output wraps the type as
// `<posthog_rs::error_tracking::Exception>::from_error::<T>`. Strip the
// angle brackets before matching so both forms hit.
let function: String = function.replace(['<', '>'], "");
Comment thread
cat-ph marked this conversation as resolved.
function.starts_with("backtrace::")
|| function.contains("capture_frames_current_first")
|| function.contains("capture_raw_frames")
Expand Down Expand Up @@ -1966,6 +1971,24 @@ mod tests {
);
}

#[test]
fn internal_capture_frames_match_both_demangler_renderings() {
// Older toolchains demangle as `Type::method`, newer ones as
// `<path::Type>::method::<T>` — the strip must catch both, or an SDK
// frame survives at the crash-site end of the canonical order.
for name in [
"posthog_rs::error_tracking::Exception::from_error",
"<posthog_rs::error_tracking::Exception>::from_error::<posthog_rs::error_tracking::tests::OuterError>",
"<posthog_rs::error_tracking::Exception>::from_message",
"<posthog_rs::client::Client>::capture_exception::<E>",
] {
assert!(is_internal_capture_frame(name), "should strip {name:?}");
}
assert!(!is_internal_capture_frame(
"my_app::checkout::Exception_from_error_report"
));
}

#[test]
fn from_error_builds_exception_list_with_stacktrace() {
let error = OuterError { source: InnerError };
Expand Down
Loading