Skip to content

Add stack trace inspection to the React Navigation plugin #253

@V3RON

Description

@V3RON

The React Navigation plugin currently shows what navigation action happened and what state it produced, but it does not show where that action came from in application code. For users debugging complex navigation flows, this is one of the most important missing pieces: when navigation happens unexpectedly, silently no-ops, loops, or targets the wrong screen, the first question is usually which line of code dispatched the action.

This matters especially in real-world React Navigation setups with nested navigators, deep linking, auth redirects, Expo Router integration, repeated imperative navigationRef usage, and multiple callsites dispatching the same action type. Without dispatch-origin visibility, users can see symptoms in the timeline but still need to manually search the codebase or add ad hoc logging to identify the source.

This is also especially important for Rozenite's agent workflows. If stack traces are only interpreted in the DevTools UI, agents still receive incomplete navigation history. The plugin should instead resolve stack traces in the React Native runtime so both agent tools and the DevTools panel receive the same enriched, actionable data.

Observed Findings

  • The plugin already receives e.data.stack from React Navigation's __unsafe_action__ event in packages/react-navigation-plugin/src/react-native/useReactNavigationEvents.ts.
  • The plugin's event payload and action history types already include a stack: string | undefined field in:
    • packages/react-navigation-plugin/src/react-native/useReactNavigationEvents.ts
    • packages/react-navigation-plugin/src/react-native/index.ts
    • packages/react-navigation-plugin/src/shared/agent-tools.ts
  • The current implementation drops the captured stack before forwarding the action callback. In packages/react-navigation-plugin/src/react-native/useReactNavigationEvents.ts, the send helper replaces the incoming stack with undefined and includes a TODO about symbolication.
  • The DevTools UI does not render stack trace information in the action timeline or detail panel. The current panel focuses on action payload and post-action state in:
    • packages/react-navigation-plugin/src/devtools-ui/components/ActionDetailPanel.tsx
    • packages/react-navigation-plugin/src/devtools-ui/components/ActionList.tsx
    • packages/react-navigation-plugin/src/devtools-ui/components/ActionItem.tsx
  • The agent tooling exposes action history through list-actions, but the current UX does not surface stack-derived origin information as a first-class debugging primitive.

Suggested Behavior

The React Navigation plugin should preserve, symbolicate, and present stack traces for navigation actions so users and agents can quickly identify the most likely application callsite that dispatched a given action.

Product behavior

  • Preserve the raw stack string for every action event when React Navigation provides one.
  • Perform best-effort symbolication on the React Native side before emitting the action event to Rozenite consumers.
  • Send the same symbolicated stack payload to both the DevTools UI and agent-facing action history.
  • Show stack trace information in the DevTools action details view.
  • Show a compact dispatch-origin preview in the action timeline, derived from the first likely application frame.
  • Keep the full raw stack available for inspection and copying.
  • Expose stack information through the agent-facing action history model so tools and agents can answer questions such as "where did this navigation come from?"

Stack interpretation

  • Raw stack capture happens in the React Native hook.
  • Symbolication should also happen on the React Native side so the plugin produces one normalized stack result for all downstream consumers.
  • The emitted payload should preserve both:
    • the original raw stack string
    • the symbolicated / parsed frame data when available
  • The plugin should identify a likely origin frame using heuristics such as:
    • prefer frames outside node_modules
    • deprioritize React Native internals
    • deprioritize @react-navigation/* internals
    • prefer workspace or app-owned source files
  • If no app-owned frame can be identified, still provide the full stack and explicitly indicate that the origin could not be resolved confidently.

UI expectations

  • In the action detail panel, add a Dispatch Origin / Stack Trace section.
  • Highlight the most likely application frame.
  • Present the full stack in an expandable frame list.
  • Support copying the raw stack string.
  • In the action list or sidebar, show a concise file:line preview or another minimal origin summary when available.
  • When no stack is present, show a clear empty state rather than implying the feature failed.

Metro symbolication

The feature should support optional symbolication through Metro via an HTTP fetch request initiated from the React Native side.

  • When a raw stack is available, the React Native plugin layer should be able to request symbolicated frames from Metro using a best-effort network call.
  • Symbolication should be treated as an enhancement, not a requirement for basic stack inspection.
  • The raw stack must always remain available, even if Metro symbolication fails, times out, or the app is not connected to a Metro server.
  • Symbolication should not block action capture indefinitely. The plugin may emit the action with raw stack information first and enrich it once symbolication resolves, or it may use a bounded timeout and fall back to raw stack data.
  • Symbolication results should be cached per unique raw stack to avoid repeated requests for the same action origin.
  • The payload sent to DevTools and agents should distinguish between:
    • raw stack only
    • symbolication in progress
    • symbolicated result available
    • symbolication failed / unavailable

The Metro integration should be designed around the fact that developers often use this plugin in local dev environments where Metro is available, but sometimes inspect sessions where Metro is unreachable or the app and DevTools are not aligned to the same packager instance.

Agent/API expectations

  • list-actions should continue returning raw stack data.
  • list-actions should also expose symbolicated / parsed stack metadata when available.
  • The plugin may additionally expose derived metadata such as:
    • hasStack
    • originFrame
    • frame summaries
    • symbolication status
  • Agent consumers should be able to inspect the likely source file and line for recent actions without needing to manually parse raw stack strings.

Non-goals / constraints

  • This should not require symbolication in order to be useful.
  • This should not mutate or discard the original raw stack string.
  • This should avoid duplicating stack parsing logic separately in DevTools and agent consumers.

Resolution Summary

The React Navigation plugin should capture raw action stack traces, symbolicate them on the React Native side through Metro when possible, preserve both raw and enriched stack data, and send the same actionable stack information to both DevTools and agent consumers so dispatch origin inspection works consistently across Rozenite surfaces.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions