Developers building complex applications with the Next.js App Router have encountered a critical reliability issue that undermines the framework's promise of seamless client-side navigation. A recent GitHub issue, identified as #99027, highlights a scenario where router.refresh() silently fails to update the user interface on pages that utilize streaming responses. This bug affects specific versions of Next.js 16, leading to a state where the server-side data is updated, but the client-side UI remains stuck on stale information until a hard reload is performed.
The Silent Failure of Router Refresh
The core of the problem lies in how the Next.js router handles cache invalidation after a mutation. In a standard workflow, a developer triggers a server action or mutation via a POST request. Upon success, the client calls router.refresh() to discard the local cache and fetch the latest data from the server. However, on pages that stream content, this mechanism can break down entirely.
According to the reported issue, the failure is not always consistent across all versions, but it is pervasive in most Next.js 16 releases. The reproduction case involves a page configured with export const dynamic = "force-dynamic", which ensures the page is rendered on every request. The page includes a timestamp stamp and a button that executes a mutation. Crucially, the page is designed to stream its content, sending a large filler block followed by a Suspense chunk that resolves after a short delay. This streaming behavior causes the React Flight response to arrive in multiple chunks rather than a single payload.
Why Streaming Triggers the Bug
Network Capture Evidence
Technical analysis of the issue reveals that the router is not simply returning old data; it is failing to request new data altogether. Chrome DevTools Protocol (CDP) network captures show that after the mutation POST is completed, no _rsc fetch is initiated for the refresh. Instead, the router silently keeps and reapplies its stale cached payload. This means the client believes it has the latest state, even though the server has already processed the change.
This behavior is particularly dangerous in production environments because it does not throw an error or log a warning. The application appears to function normally, but the user sees outdated information indefinitely. For example, if a user updates a profile setting, the server saves the change, but the UI continues to display the old settings. The only way to resolve the discrepancy is for the user to perform a hard reload of the page, which is a poor user experience and a sign of a broken state management flow.
Version Specifics and Query String Anomalies
The issue is not uniform across all Next.js 16 versions. Testing indicates that version 16.2.12 is the only tested version where router.refresh() works at all on the affected page. However, even in this version, the behavior is inconsistent and depends on the presence of a query string.
16.2.12 is the only tested version where refresh works at all on this page, and there the query string alone is the trigger: same page, same build, refresh works on / and never works on /?status=draft.
This anomaly suggests that the router's cache keying mechanism may be mishandling URLs with query parameters in the context of streamed responses. The fact that the same build and page configuration behave differently based solely on the query string points to a subtle bug in how the App Router's segment cache is invalidated or matched.
Practical Impact for Developers
For teams using Next.js 16 in production, this bug poses a significant risk. Applications that rely on streaming for performance, such as those using Suspense boundaries to load data progressively, are most vulnerable. The symptom is that mutations persist on the server, but the UI shows pre-mutation data. This can lead to confusion, data integrity issues, and a loss of user trust.
Developers should be aware that next dev does not exercise the cached path in the same way as a production build. Therefore, this bug may not appear during local development but will surface in production environments where next start is used. This discrepancy between development and production behavior makes the bug particularly insidious.
Workarounds and Mitigation
Until a fix is released, developers can consider the following workarounds:
- Avoid Streaming for Critical Mutations: If possible, ensure that pages involving frequent mutations do not rely on streaming responses. This may involve restructuring the page to load all data before rendering.
- Manual Cache Busting: Implement a manual cache-busting mechanism by adding a unique query parameter to the URL after each mutation. This forces the router to treat the request as a new one, bypassing the stale cache.
- Hard Reload Fallback: In cases where the UI state is critical, consider triggering a hard reload after a mutation. While this is not ideal, it ensures that the user always sees the latest data.
What to Watch Next
The Next.js team is likely to address this issue in an upcoming patch release. Developers should monitor the GitHub issue for updates and test their applications against the latest versions of Next.js 16. It is also advisable to review any custom caching logic or middleware that may interact with the router's refresh mechanism.
As the App Router continues to evolve, understanding the nuances of its caching and streaming behavior is essential for building reliable applications. This bug serves as a reminder that even in mature frameworks, edge cases can lead to unexpected behavior, and thorough testing in production-like environments is crucial.
For now, developers should exercise caution when using router.refresh() on streamed pages and consider alternative approaches to ensure data consistency. The community's feedback and reproduction cases will be vital in helping the Next.js team identify and resolve the root cause of this issue.