Skip to content

fix(tracing): Support Expo Router 58 in expoRouterIntegration - #6923

Open
alwx wants to merge 3 commits into
mainfrom
alwx/bugfix/6901
Open

alwx wants to merge 3 commits into
mainfrom
alwx/bugfix/6901

Conversation

@alwx

@alwx alwx commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix

📜 Description

expoRouterIntegration now gets the navigation container ref from the public useNavigationContainerRef export. It computes route info with getRouteInfoFromState, which Expo Router 57 and 58 both have at the same path. On Expo Router 58, the integration listens to the internal actionDispatched event and starts navigation spans through a new internal _startIdleNavigationSpan method on reactNavigationIntegration.

💡 Motivation and Context

Expo Router 58 removed expo-router/build/global-state/router-store and stopped sending __unsafe_action__. Metro then warned on every bundle, and the integration could not start navigation spans. Fixes #6901.

💚 How did you test it?

I ran expo export on samples/expo (expo-router 58.0.23). The warning appeared 10 times before this change and 0 times after it. I added unit tests for the new route info source, the action listener, and the listener cleanup.

📝 Checklist

  • I added tests to verify changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • No breaking changes.

🔮 Next steps

On Expo Router 58, actionDispatched fires after the new screen renders, so spans start a bit late. A follow-up can use the expo-router:action-enqueued performance mark for an earlier start time.

alwx added a commit that referenced this pull request Oct 21, 2026
Replace the placeholder in the CHANGELOG entry with the PR number, #6923.
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

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).


  • fix(tracing): Support Expo Router 58 in expoRouterIntegration by alwx in #6923
  • chore(deps): update Wizard to v9.0.0 by github-actions in #6936
  • chore(deps): update CLI to v3.8.2 by github-actions in #6933
  • ci: Move CI off end-of-life Node versions to Node 22 by antonis in #6927

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor
Fails
🚫 Pull request is not ready for merge, please add the "ready-to-merge" label to the pull request

Generated by 🚫 dangerJS against 1246c96

alwx added a commit that referenced this pull request Oct 22, 2026
Replace the placeholder in the CHANGELOG entry with the PR number, #6923.
@alwx
alwx marked this pull request as ready for review October 22, 2026 09:04
@alwx
alwx force-pushed the alwx/bugfix/6901 branch from 87f19fa to c899efa Compare October 22, 2026 09:04
@alwx
alwx force-pushed the alwx/bugfix/6901 branch from 87f19fa to c899efa Compare October 22, 2026 09:04
@alwx
alwx requested a review from a team as a code owner October 22, 2026 09:04

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/js/tracing/expoRouterStore.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fc6a0b5. Configure here.

Comment thread packages/core/src/js/tracing/expoRouterIntegration.ts

@antonis antonis left a comment

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.

Thanks for tackling this! The Expo Router 58 path looks right: actionDispatched fires before the container's state event, and v57 has no internalNavigationEvents, so no navigation gets two spans.

Two blockers for older versions, details inline:

  1. Expo Router ≤55 silently loses the integration (getRouteInfoFromState is missing).
  2. v56/57 regress #6431 (navigationRef is read only once).

I verified both against the published expo-router packages (4.x through 58.x).

Comment thread packages/core/src/js/tracing/expoRouterStore.ts Outdated
Comment thread packages/core/src/js/tracing/expoRouterStore.ts Outdated
Comment thread packages/core/src/js/tracing/expoRouterStore.ts Outdated
Comment thread packages/core/test/tracing/expoRouterIntegration.test.ts
Comment thread CHANGELOG.md Outdated
alwx added 3 commits October 23, 2026 13:58
Expo Router 58 removed the internal router-store module and no longer emits
React Navigation's __unsafe_action__ event. Because 58 declares package
exports, Metro printed an invalid package.json warning for the router-store
require on every bundle, and the integration could not start navigation spans.

The navigation container ref now comes from the public
useNavigationContainerRef export, which returns a module-level ref in
Expo Router 57 and 58. Route info is computed from the root navigation state
with getRouteInfoFromState, which both versions ship at the same path.

On Expo Router 58 the integration listens to the internal actionDispatched
event and starts the idle navigation span through a new internal
_startIdleNavigationSpan method on reactNavigationIntegration. Expo Router 57
has no exports field, so the missing internalNavigationEvents path is a silent
optional dependency there and spans still start from __unsafe_action__.

Fixes #6901
Replace the placeholder in the CHANGELOG entry with the PR number, #6923.
Review of #6923 found two regressions for Expo Router versions before 58.

Expo Router 55 and older do not ship getRouteInfoFromState, so the require
threw and the integration stopped as if expo-router were not installed. The
route info module is now optional. Without it, the integration still
registers the navigation container and skips the route override, so spans
use the React Navigation route names.

In Expo Router 56 and 57, useNavigationContainerRef reads a store that stays
empty until the Root Layout renders. The ref was read once at Sentry.init, so
polling timed out and brought back #6431. The store now reads the ref on every
access, and a throwing call returns undefined so it cannot escape the poll
timer.

The #6431 regression test is restored with the new mocks, and new tests cover
a missing getRouteInfoFromState and a throwing useNavigationContainerRef. The
CHANGELOG entry moves back to the Unreleased section after the 8.32.0 release.
@alwx
alwx force-pushed the alwx/bugfix/6901 branch from 0e409bf to 1246c96 Compare October 23, 2026 12:00
@alwx
alwx requested a review from antonis October 23, 2026 12:14
@invivek26

Copy link
Copy Markdown

We backported this PR (at 1246c96) onto 8.28.0 to run expo-router 58.0.25, and real route changes now get navigation spans with the templated route name. Thanks for this.

One thing we hit on 58: actionDispatched fires for every reducer action, not only navigations. On a cold launch with tabs we saw ROUTE_NAMES_CHANGED twice and PRELOAD once per tab. router.prefetch() gives more PRELOADs and another ROUTE_NAMES_CHANGED, and param updates send SET_PARAMS.

expoRouterIntegration leaves useDispatchedActionData off, so startIdleNavigationSpan doesn't filter any of these. Each one starts an idle span, and the next state event keeps the same route key. The span then ships on its idle timeout as an unnamed Route Change navigation transaction with no route data. We got one on every cold launch and one per router.prefetch().

We worked around it by forwarding only navigation actions from the listener:

const NAVIGATION_ACTION_TYPES = new Set([
    'NAVIGATE',
    'NAVIGATE_TO_HREF',
    'PUSH',
    'REPLACE',
    'GO_BACK',
    'POP',
    'POP_TO',
    'POP_TO_TOP',
    'JUMP_TO',
    'RESET',
]);

// in the actionDispatched listener, after the `!event.actionType` check
if (!NAVIGATION_ACTION_TYPES.has(event.actionType)) {
    return;
}

With that, a cold launch ships one ui.load and nothing else. Tab switches (NAVIGATE), pushes, deep links and replace each ship one templated navigation transaction. GO_BACK still starts a span, and the empty-back rule drops it as before.

A denylist (PRELOAD, SET_PARAMS, ROUTE_NAMES_CHANGED) would also work for what we saw. But router 58's reducer has other internal actions too (REMOVE_ROUTES, NAVIGATOR_CHANGED, REPORT_CONSUMED), so an allowlist seemed safer.

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.

expo-router integration requires expo-router/build/global-state/router-store, but it was removed in SDK 58

3 participants