Skip to content

feat: Auto-create traces for MAUI navigation events#5111

Draft
jamescrosswell wants to merge 15 commits intomainfrom
maui-transactions-5109
Draft

feat: Auto-create traces for MAUI navigation events#5111
jamescrosswell wants to merge 15 commits intomainfrom
maui-transactions-5109

Conversation

@jamescrosswell
Copy link
Copy Markdown
Collaborator

@jamescrosswell jamescrosswell commented Apr 2, 2026

Resolves: #5109

Out of scope

The Android and Cocoa SDKs instrument traces for navigation, gesture and scroll events. This PR does so only for navigation events.

See #5116 for details:

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Features ✨

  • feat: Auto-create traces for MAUI navigation events by jamescrosswell in #5111

Fixes 🐛

  • fix: Workaround FileNotFoundException on Android when recovering sessions by jamescrosswell in #5084
  • fix: CaptureFeedback now supports multiple attachments correctly by bitsandfoxes in #5077

Dependencies ⬆️

Deps

  • chore(deps): update Native SDK to v0.13.5 by github-actions in #5119
  • chore(deps): update CLI to v3.3.5 by github-actions in #5093
  • chore(deps): update Native SDK to v0.13.4 by github-actions in #5081
  • chore(deps): update Java SDK to v8.37.1 by github-actions in #5071
  • chore(deps): update CLI to v3.3.4 by github-actions in #5068
  • chore(deps): update Java SDK to v8.37.0 by github-actions in #5069
  • chore(deps): update Cocoa SDK to v9.8.0 by github-actions in #5044
  • chore(deps): update Java SDK to v8.36.0 by github-actions in #5036
  • chore(deps): update epitaph to 0.1.1 by github-actions in #5036

Other

  • chore: Update validate-pr workflow by stephanie-anderson in #5108
  • ci: fix workflows that always fail for fork PRs by jamescrosswell in #5065

🤖 This preview updates automatically when you update the PR.

/// Starts a transaction that will automatically finish after <paramref name="idleTimeout"/> if not
/// finished explicitly first.
/// </summary>
public ITransactionTracer StartTransaction(ITransactionContext context, TimeSpan? idleTimeout);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it'd be a breaking change if we added this method to the public IHub interface. Potentially in v7 we could consider making this public and getting rid of the IHubInternal interface.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 66.99029% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.97%. Comparing base (ed4f9a1) to head (0418c99).

Files with missing lines Patch % Lines
src/Sentry/TransactionTracer.cs 61.53% 9 Missing and 6 partials ⚠️
src/Sentry/Infrastructure/SystemTimer.cs 0.00% 6 Missing ⚠️
src/Sentry/SentrySdk.cs 0.00% 5 Missing and 1 partial ⚠️
src/Sentry.Maui/Internal/MauiEventsBinder.cs 92.50% 0 Missing and 3 partials ⚠️
src/Sentry/Extensibility/DisabledHub.cs 0.00% 1 Missing ⚠️
src/Sentry/Extensibility/HubAdapter.cs 0.00% 1 Missing ⚠️
src/Sentry/Internal/Hub.cs 50.00% 1 Missing ⚠️
src/Sentry/Internal/NoOpTransaction.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5111      +/-   ##
==========================================
- Coverage   73.99%   73.97%   -0.03%     
==========================================
  Files         499      500       +1     
  Lines       18067    18149      +82     
  Branches     3520     3550      +30     
==========================================
+ Hits        13368    13425      +57     
- Misses       3839     3858      +19     
- Partials      860      866       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +129 to +130
ITransactionTracer IHubInternal.StartTransaction(ITransactionContext context, TimeSpan? idleTimeout)
=> SentrySdk.StartTransaction(context, idleTimeout);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Infinite recursion in HubAdapter.StartTransaction causes StackOverflowException

The new IHubInternal.StartTransaction(context, idleTimeout) implementation in HubAdapter forwards to SentrySdk.StartTransaction(context, idleTimeout). However, SentrySdk.StartTransaction checks if CurrentHub is IHubInternal and calls internalHub.StartTransaction(context, idleTimeout). Since HubAdapter implements IHubInternal and is commonly set as CurrentHub, this creates an infinite recursion loop that will result in a StackOverflowException.

Verification

Read HubAdapter.cs lines 129-130 confirming it calls SentrySdk.StartTransaction. Read SentrySdk.cs lines 671-674 confirming the IHubInternal dispatch pattern. HubAdapter implements IHubInternal (line 16), so SentrySdk will call back into HubAdapter, creating infinite recursion.

Identified by Warden find-bugs · N5D-RKE

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HubAdapter is never set to SentrySdk.CurrentHub... the whole point of HubAdapter is to allow us to mock the otherwise static/unmockable SentrySdk class in unit tests.

I guess we could enforce this explicitly. Addressed in: 0418c99

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix attempt detected (commit 0418c99)

HubAdapter now implements IHubInternal and added the StartTransaction(context, idleTimeout) method that calls SentrySdk.StartTransaction, which will dispatch back to the same method via the IHubInternal check in SentrySdk, perpetuating the infinite recursion loop.

The original issue appears unresolved. Please review and try again.

Evaluated by Warden

var latestSpanEnd = _spans
.Where(s => s.IsFinished)
.Select(s => s.EndTimestamp)
.Max();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Max() on empty sequence throws InvalidOperationException when idle transaction has no finished spans

When Finish() is called directly on an idle transaction (_idleTimeout.HasValue is true) that has child spans but none are finished, the LINQ expression _spans.Where(s => s.IsFinished).Select(s => s.EndTimestamp).Max() operates on an empty sequence. LINQ's Max() throws InvalidOperationException ("Sequence contains no elements") when the input sequence is empty. This causes a runtime crash when manually finishing an idle transaction before any child span completes.

Verification

Read TransactionTracer.cs lines 430-442 to understand the code flow. Confirmed _spans is ConcurrentBag<ISpan> (line 169/171), and the Where clause filters for IsFinished which checks EndTimestamp is not null (SpanTracer.cs line 41). Read tests confirming idle transactions can have unfinished spans. LINQ Max() on empty IEnumerable<T> throws InvalidOperationException per .NET documentation.

Identified by Warden code-review · JKQ-D53

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.

Transactions for UI events in MAUI

2 participants