/
githubmirror
/
next.js
Обзор
Документация
Войти
/
githubmirror
/
next.js
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
canary
test/e2e/app-dir/cache-components-errors/error-attribution.util.ts
605 строк
30 KB
Aurora Scharff
Insights: use a single Learn more link in console errors (#95967)
21 июл 2026, 13:53
Не верифицирован
21 июл 2026, 13:53
18d2e2d
Код
Авторство
О чём код?
import { isNextDev } from 'e2e-utils' import { getRedboxDescription, retry, waitForNoErrorToast, } from 'next-test-utils' import { getPrerenderOutput } from './utils' import type { CacheComponentsErrorsContext } from './shared.util' export function registerErrorAttributionTests( ctx: CacheComponentsErrorsContext ) { const { next, isTurbopack, isDebugPrerender, prerender, skipped } = ctx let cliOutputLength: number beforeEach(() => { cliOutputLength = next.cliOutput.length }) describe('Error Attribution with Sync IO', () => { describe('Guarded RSC with guarded Client sync IO', () => { const pathname = '/sync-attribution/guarded-async-guarded-clientsync' if (skipped) { return } if (isNextDev) { it('does not show a validation error in the dev overlay', async () => { const browser = await next.browser(pathname) await waitForNoErrorToast(browser) }) } else { it('should not error the build sync IO is used inside a Suspense Boundary in a client Component and nothing else is dynamic', async () => { try { await prerender(pathname) } catch (error) { throw new Error('expected build not to fail', { cause: error }) } expect(next.cliOutput).toContain(`◐ ${pathname}`) }) } }) describe('Guarded RSC with unguarded Client sync IO', () => { const pathname = '/sync-attribution/guarded-async-unguarded-clientsync' if (skipped) { return } if (isNextDev) { it('should show a collapsed redbox error', async () => { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` { "code": "E1434", "description": "Next.js encountered the unstable value new Date() in a Client Component.", "environmentLabel": "Server", "label": "Blocking Route", "source": "app/sync-attribution/guarded-async-unguarded-clientsync/client.tsx (5:16) @ SyncIO > 5 | const data = new Date().toISOString() | ^", "stack": [ "SyncIO app/sync-attribution/guarded-async-unguarded-clientsync/client.tsx (5:16)", "Page app/sync-attribution/guarded-async-unguarded-clientsync/page.tsx (22:9)", ], } `) }) } else { it('should error the build with a reason related to sync IO access', async () => { try { await prerender(pathname) } catch { // we expect the build to fail } const output = getPrerenderOutput( next.cliOutput.slice(cliOutputLength), { isMinified: !isDebugPrerender } ) if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/guarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at SyncIO (app/sync-attribution/guarded-async-unguarded-clientsync/client.tsx:5:16) at Page (app/sync-attribution/guarded-async-unguarded-clientsync/page.tsx:22:9) 3 | export function SyncIO() { 4 | // This is a sync IO access that should not cause an error > 5 | const data = new Date().toISOString() | ^ 6 | 7 | return ( 8 | <main> To debug the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/guarded-async-unguarded-clientsync" in your browser to investigate the error. Error occurred prerendering page "/sync-attribution/guarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error > Export encountered errors on 1 path: /sync-attribution/guarded-async-unguarded-clientsync/page: /sync-attribution/guarded-async-unguarded-clientsync" `) } else { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/guarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at SyncIO (webpack:///app/sync-attribution/guarded-async-unguarded-clientsync/client.tsx:5:16) at Page (webpack:///app/sync-attribution/guarded-async-unguarded-clientsync/page.tsx:22:9) 3 | export function SyncIO() { 4 | // This is a sync IO access that should not cause an error > 5 | const data = new Date().toISOString() | ^ 6 | 7 | return ( 8 | <main> To debug the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/guarded-async-unguarded-clientsync" in your browser to investigate the error. Error occurred prerendering page "/sync-attribution/guarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error > Export encountered errors on 1 path: /sync-attribution/guarded-async-unguarded-clientsync/page: /sync-attribution/guarded-async-unguarded-clientsync" `) } } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/guarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at <unknown> (app/sync-attribution/guarded-async-unguarded-clientsync/client.tsx:5:16) 3 | export function SyncIO() { 4 | // This is a sync IO access that should not cause an error > 5 | const data = new Date().toISOString() | ^ 6 | 7 | return ( 8 | <main> To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/sync-attribution/guarded-async-unguarded-clientsync" in your browser to investigate the error. - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. Error occurred prerendering page "/sync-attribution/guarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error Export encountered an error on /sync-attribution/guarded-async-unguarded-clientsync/page: /sync-attribution/guarded-async-unguarded-clientsync, exiting the build." `) } else { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/guarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at a (<next-dist-dir>) To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/sync-attribution/guarded-async-unguarded-clientsync" in your browser to investigate the error. - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. Error occurred prerendering page "/sync-attribution/guarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error Export encountered an error on /sync-attribution/guarded-async-unguarded-clientsync/page: /sync-attribution/guarded-async-unguarded-clientsync, exiting the build." `) } } }) } }) describe('Unguarded RSC with guarded Client sync IO', () => { const pathname = '/sync-attribution/unguarded-async-guarded-clientsync' if (skipped) { return } if (isNextDev) { it('should show a collapsed redbox error', async () => { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` { "code": "E1427", "description": "Next.js encountered runtime data during prerendering.", "environmentLabel": "Server", "label": "Blocking Route", "source": "app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx (34:18) @ RequestData > 34 | ;(await cookies()).get('foo') | ^", "stack": [ "RequestData app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx (34:18)", "Page app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx (27:9)", ], } `) }) } else { it('should error the build with a reason related dynamic data', async () => { try { await prerender(pathname) } catch { // we expect the build to fail } const output = getPrerenderOutput( next.cliOutput.slice(cliOutputLength), { isMinified: !isDebugPrerender } ) if (isTurbopack) { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-guarded-clientsync": Next.js encountered uncached or runtime data during prerendering. \`fetch(...)\`, \`cookies()\`, \`headers()\`, \`params\`, \`searchParams\`, or \`connection()\` accessed outside of \`<Suspense>\` prevents the route from being prerendered, blocking the page load and leading to a slower user experience. Ways to fix this: - [stream] Provide a placeholder with \`<Suspense fallback={...}>\` around the data access - [cache] For uncached data (\`fetch\`, database calls): cache the access with \`"use cache"\` (does not apply to \`connection()\`) - [block] Set \`export const instant = false\` to allow a blocking route Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic at RequestData (app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx:34:18) at Page (app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx:27:9) 32 | 33 | async function RequestData() { > 34 | ;(await cookies()).get('foo') | ^ 35 | return ( 36 | <div> 37 | <h2>Request Data Access</h2> To debug the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-guarded-clientsync" in your browser to investigate the error. Error occurred prerendering page "/sync-attribution/unguarded-async-guarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error > Export encountered errors on 1 path: /sync-attribution/unguarded-async-guarded-clientsync/page: /sync-attribution/unguarded-async-guarded-clientsync" `) } else { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-guarded-clientsync": Next.js encountered uncached or runtime data during prerendering. \`fetch(...)\`, \`cookies()\`, \`headers()\`, \`params\`, \`searchParams\`, or \`connection()\` accessed outside of \`<Suspense>\` prevents the route from being prerendered, blocking the page load and leading to a slower user experience. Ways to fix this: - [stream] Provide a placeholder with \`<Suspense fallback={...}>\` around the data access - [cache] For uncached data (\`fetch\`, database calls): cache the access with \`"use cache"\` (does not apply to \`connection()\`) - [block] Set \`export const instant = false\` to allow a blocking route Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic at a (<anonymous>) at main (<anonymous>) at main (<anonymous>) at body (<anonymous>) at html (<anonymous>) To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-guarded-clientsync" in your browser to investigate the error. - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. Error occurred prerendering page "/sync-attribution/unguarded-async-guarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error Export encountered an error on /sync-attribution/unguarded-async-guarded-clientsync/page: /sync-attribution/unguarded-async-guarded-clientsync, exiting the build." `) } } else { if (isDebugPrerender) { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-guarded-clientsync": Next.js encountered uncached or runtime data during prerendering. \`fetch(...)\`, \`cookies()\`, \`headers()\`, \`params\`, \`searchParams\`, or \`connection()\` accessed outside of \`<Suspense>\` prevents the route from being prerendered, blocking the page load and leading to a slower user experience. Ways to fix this: - [stream] Provide a placeholder with \`<Suspense fallback={...}>\` around the data access - [cache] For uncached data (\`fetch\`, database calls): cache the access with \`"use cache"\` (does not apply to \`connection()\`) - [block] Set \`export const instant = false\` to allow a blocking route Learn more: https://nextjs.org/docs/messages/blocking-prerender-dynamic at RequestData (webpack:///app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx:34:18) at Page (webpack:///app/sync-attribution/unguarded-async-guarded-clientsync/page.tsx:27:9) 32 | 33 | async function RequestData() { > 34 | ;(await cookies()).get('foo') | ^ 35 | return ( 36 | <div> 37 | <h2>Request Data Access</h2> To debug the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-guarded-clientsync" in your browser to investigate the error. Error occurred prerendering page "/sync-attribution/unguarded-async-guarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error > Export encountered errors on 1 path: /sync-attribution/unguarded-async-guarded-clientsync/page: /sync-attribution/unguarded-async-guarded-clientsync" `) } else { // Webpack does not ignore the stack frames that point into // Next.js internals, and the resolved frames are flaky in // non-debug-prerender mode, so we don't assert on the stack // frames here. expect(output).toInclude( 'Error: Route "/sync-attribution/unguarded-async-guarded-clientsync": Next.js encountered uncached or runtime data during prerendering.' ) } } }) } }) describe('Unguarded RSC with Client sync IO in a microtask', () => { const pathname = '/sync-attribution/unguarded-async-client-microtask-syncio' if (skipped) { return } if (isNextDev) { it('should show request data rather than attribute a microtask sync IO access', async () => { const browser = await next.browser(pathname) await retry(async () => { const redbox = await getRedboxDescription(browser) expect(redbox).toContain( 'Next.js encountered runtime data during prerendering.' ) expect(redbox).not.toContain('new Date()') }) }) } else { it('should error for request data rather than attribute a microtask sync IO access', async () => { try { await prerender(pathname) } catch { // we expect the build to fail } const output = getPrerenderOutput( next.cliOutput.slice(cliOutputLength), { isMinified: !isDebugPrerender } ) expect(output).toContain( `Error: Route "${pathname}": Next.js encountered uncached or runtime data during prerendering.` ) expect(output).not.toContain( 'Next.js encountered the unstable value `new Date()` in a Client Component.' ) }) } }) describe('unguarded RSC with unguarded Client sync IO', () => { const pathname = '/sync-attribution/unguarded-async-unguarded-clientsync' if (skipped) { return } if (isNextDev) { it('should show a collapsed redbox error', async () => { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` { "code": "E1434", "description": "Next.js encountered the unstable value new Date() in a Client Component.", "environmentLabel": "Server", "label": "Blocking Route", "source": "app/sync-attribution/unguarded-async-unguarded-clientsync/client.tsx (5:16) @ SyncIO > 5 | const data = new Date().toISOString() | ^", "stack": [ "SyncIO app/sync-attribution/unguarded-async-unguarded-clientsync/client.tsx (5:16)", "Page app/sync-attribution/unguarded-async-unguarded-clientsync/page.tsx (22:9)", ], } `) }) } else { it('should error the build with a reason related to sync IO access', async () => { try { await prerender(pathname) } catch { // we expect the build to fail } const output = getPrerenderOutput( next.cliOutput.slice(cliOutputLength), { isMinified: !isDebugPrerender } ) if (isDebugPrerender) { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at SyncIO (app/sync-attribution/unguarded-async-unguarded-clientsync/client.tsx:5:16) at Page (app/sync-attribution/unguarded-async-unguarded-clientsync/page.tsx:22:9) 3 | export function SyncIO() { 4 | // This is a sync IO access that should not cause an error > 5 | const data = new Date().toISOString() | ^ 6 | 7 | return ( 8 | <main> To debug the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-unguarded-clientsync" in your browser to investigate the error. Error occurred prerendering page "/sync-attribution/unguarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error > Export encountered errors on 1 path: /sync-attribution/unguarded-async-unguarded-clientsync/page: /sync-attribution/unguarded-async-unguarded-clientsync" `) } else { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at SyncIO (webpack:///app/sync-attribution/unguarded-async-unguarded-clientsync/client.tsx:5:16) at Page (webpack:///app/sync-attribution/unguarded-async-unguarded-clientsync/page.tsx:22:9) 3 | export function SyncIO() { 4 | // This is a sync IO access that should not cause an error > 5 | const data = new Date().toISOString() | ^ 6 | 7 | return ( 8 | <main> To debug the issue, start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-unguarded-clientsync" in your browser to investigate the error. Error occurred prerendering page "/sync-attribution/unguarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error > Export encountered errors on 1 path: /sync-attribution/unguarded-async-unguarded-clientsync/page: /sync-attribution/unguarded-async-unguarded-clientsync" `) } } else { if (isTurbopack) { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at <unknown> (app/sync-attribution/unguarded-async-unguarded-clientsync/client.tsx:5:16) 3 | export function SyncIO() { 4 | // This is a sync IO access that should not cause an error > 5 | const data = new Date().toISOString() | ^ 6 | 7 | return ( 8 | <main> To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-unguarded-clientsync" in your browser to investigate the error. - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. Error occurred prerendering page "/sync-attribution/unguarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error Export encountered an error on /sync-attribution/unguarded-async-unguarded-clientsync/page: /sync-attribution/unguarded-async-unguarded-clientsync, exiting the build." `) } else { expect(output).toMatchInlineSnapshot(` "Error: Route "/sync-attribution/unguarded-async-unguarded-clientsync": Next.js encountered the unstable value \`new Date()\` in a Client Component. This value would be evaluated during the prerender, instead of recomputed on each visit. Ways to fix this: - [stream] Wrap the Client Component in \`<Suspense fallback={...}>\` - [defer] Move the read into a \`useEffect\` or event handler - [measure] If the value is for telemetry, use a timing API such as \`performance.now()\` Learn more: https://nextjs.org/docs/messages/blocking-prerender-current-time-client at a (<next-dist-dir>) To get a more detailed stack trace and pinpoint the issue, try one of the following: - Start the app in development mode by running \`next dev\`, then open "/sync-attribution/unguarded-async-unguarded-clientsync" in your browser to investigate the error. - Rerun the production build with \`next build --debug-prerender\` to generate better stack traces. Error occurred prerendering page "/sync-attribution/unguarded-async-unguarded-clientsync". Read more: https://nextjs.org/docs/messages/prerender-error Export encountered an error on /sync-attribution/unguarded-async-unguarded-clientsync/page: /sync-attribution/unguarded-async-unguarded-clientsync, exiting the build." `) } } }) } }) }) // TODO(restart-on-cache-miss): Figure out how to test this without flakiness describe.skip('Unhandled Rejection Suppression', () => { const pathname = '/unhandled-rejection' if (isNextDev) { it('should suppress unhandled rejections during prerender validation in dev', async () => { const browser = await next.browser(pathname) await expect(browser).toDisplayCollapsedRedbox(` [ { "description": "BOOM", "environmentLabel": "Prerender", "label": "Console Error", "source": null, "stack": [ "Page <anonymous>", ], }, { "description": "⨯ "unhandledRejection:" "BOOM"", "environmentLabel": "Prerender", "label": "Console Error", "source": null, "stack": [ "Page <anonymous>", ], }, { "description": "⨯ "unhandledRejection: " "BOOM"", "environmentLabel": "Prerender", "label": "Console Error", "source": null, "stack": [ "Page <anonymous>", ], }, { "description": "BAM", "environmentLabel": "Server", "label": "Console Error", "source": null, "stack": [ "Page <anonymous>", ], }, { "description": "⨯ "unhandledRejection:" "BAM"", "environmentLabel": "Server", "label": "Console Error", "source": null, "stack": [ "Page <anonymous>", ], }, { "description": "⨯ "unhandledRejection: " "BAM"", "environmentLabel": "Server", "label": "Console Error", "source": null, "stack": [ "Page <anonymous>", ], }, ] `) }) } else { it('should suppress unhandled rejections after prerender abort', async () => { try { await prerender(pathname) } catch {} const output = getPrerenderOutput( next.cliOutput.slice(cliOutputLength), { isMinified: !isDebugPrerender } ) expect(output).toMatchInlineSnapshot(` "BOOM BOOM" `) }) } }) }