nextjs - ✅(Solved) Fix App Router reverts history.replaceState() calls made during module evaluation [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#91819Fetched 2026-04-08 01:16:07
View on GitHub
Comments
2
Participants
2
Timeline
13
Reactions
2
Author
Participants
Timeline (top)
subscribed ×4mentioned ×3commented ×2cross-referenced ×2

Fix Action

Fixed

PR fix notes

PR #12498: fix(auth): workaround Next.js replaceState bug

Description (problem / solution / changelog)

Description

Adds a workaround for https://github.com/vercel/next.js/issues/91819

Closes #12431

What to review

The workaround simply (re)consumes the sid hash parameter in the auth callback url, re-issues the replaceState call which removes the sid again. Instead of exporting consumeSessionId() I'm calling it from a dedicated export that we can delete if/when the upstream issue is fixed.

Testing

  • I've tested and verified that it works for an embedded Studios in Next.js 16.2 with auth.loginMethod: 'token'

Notes for release

Adds a workaround for a bug in Next.js that could in some cases result in an error saying Session with sid (…) not found

Changed files

  • packages/sanity/src/core/store/_legacy/authStore/createAuthStore.ts (modified, +3/-1)
  • packages/sanity/src/core/store/_legacy/authStore/sessionId.ts (modified, +8/-0)

Code Example

Operating System: not relevant here
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: 1.22.22
  pnpm: 10.30.3
Relevant Packages:
  next: 16.2.1-canary.5 // Latest available version is detected (16.2.1-canary.5).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/bjoerge/next-16-replacestate-issue-repro/

To Reproduce

  1. Run next dev
  2. Click the link that takes you to the hash param: #foo=test
  3. This will trigger a page reload
  4. After reload, notice the hash is gone after replacesState has been called while the alert is blocking
  5. Notice the hash reverts back after the alert is dismissed and the script resumes

Current vs. Expected behavior

Any calls to replaceState() during module eval should be respected.

Provide environment information

Operating System: not relevant here
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: 1.22.22
  pnpm: 10.30.3
Relevant Packages:
  next: 16.2.1-canary.5 // Latest available version is detected (16.2.1-canary.5).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Not sure / App router

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local), Vercel (Deployed)

Additional context

This appears to be a regression in next@16, the repro works as expected with next@15.

extent analysis

Fix Plan

To address the issue with replaceState() calls being ignored during module evaluation, we need to modify the code to handle the hash change after the alert is dismissed.

Here are the steps:

  • Use the useEffect hook to listen for hash changes.
  • Update the URL hash after the alert is dismissed.

Example code:

import { useEffect } from 'eact';

function MyComponent() {
  useEffect(() => {
    // Handle hash change after alert is dismissed
    const handleHashChange = () => {
      const hash = window.location.hash;
      if (hash) {
        // Update the URL hash
        window.history.replaceState(null, null, hash);
      }
    };

    // Listen for hash changes
    window.addEventListener('hashchange', handleHashChange);

    return () => {
      // Remove the event listener
      window.removeEventListener('hashchange', handleHashChange);
    };
  }, []);

  // Rest of your component code...
}

Verification

To verify the fix, follow these steps:

  • Run next dev and navigate to the page with the hash param #foo=test.
  • Click the link that triggers the page reload.
  • After the alert is dismissed, check if the hash is preserved in the URL.

Extra Tips

  • Make sure to test the fix in different environments (e.g., next start, Vercel) to ensure it works as expected.
  • Consider adding additional logging or debugging statements to verify the fix in your specific use case.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING