From 2f68e62d30a84380d757ff120b8c9c24bffe0665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Wed, 8 Nov 2023 17:08:24 -0500 Subject: [PATCH] Reword PPR caught bail out to avoid "postpone" terminology (#58223) The "postpone" terminology is internal to React and can be used for more things than just this. It's also a mechanism we may or may not rely on. --------- Co-authored-by: Zack Tanner --- errors/ppr-caught-error.mdx | 27 +++++++++++++++++++ errors/ppr-postpone-error.mdx | 13 --------- .../src/client/components/maybe-postpone.ts | 6 ++--- .../components/static-generation-bailout.ts | 2 +- .../next/src/server/app-render/app-render.tsx | 5 +++- .../e2e/app-dir/ppr-errors/ppr-errors.test.ts | 10 +++---- 6 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 errors/ppr-caught-error.mdx delete mode 100644 errors/ppr-postpone-error.mdx diff --git a/errors/ppr-caught-error.mdx b/errors/ppr-caught-error.mdx new file mode 100644 index 0000000000..9f61ec2093 --- /dev/null +++ b/errors/ppr-caught-error.mdx @@ -0,0 +1,27 @@ +--- +title: Static Bail Out Caught +--- + +## Why This Error Occurred + +When Partial Prerendering (PPR) is enabled, using APIs that opt into Dynamic Rendering like `cookies`, `headers`, or `fetch` (such as with `cache: 'no-store'` or `revalidate: 0`) will cause React to throw a special error object to know which part of the page cannot be statically generated - while still letting the rest of it be partially static. If you catch this error, it is not safe for us to generate any static data, and your build will fail. + +## Possible Ways to Fix It + +- Ensure that you are not wrapping Next.js APIs that opt into dynamic rendering in a `try/catch` block. +- If you do wrap these APIs in a try/catch, make sure you re-throw the original error so it can be caught by Next.js. +- Alternatively, insert [`unstable_noStore()`](docs/app/api-reference/functions/unstable_noStore) before the try/catch. + +```js +import { unstable_noStore } from 'next/cache' + +async function fetchData() { + unstable_noStore() // opt out before we even get to the try/catch + try { + const response = await fetch(url); + ... + } catch (x) { + ... + } +} +``` diff --git a/errors/ppr-postpone-error.mdx b/errors/ppr-postpone-error.mdx deleted file mode 100644 index a0197fd542..0000000000 --- a/errors/ppr-postpone-error.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Static Generation Postpone Error ---- - -## Why This Error Occurred - -When Partial Prerendering (PPR) is enabled, using APIs that opt into Dynamic Rendering like `cookies`, `headers`, or `fetch` (such as with `cache: 'no-store'` or `revalidate: 0`) will cause Next.js to throw a special error to know which part of the page cannot be statically generated. If you catch this error, we will not be able to generate any static data, and your build will fail. - -## Possible Ways to Fix It - -- Ensure that you are not wrapping Next.js APIs that opt into dynamic rendering in a `try/catch` block. -- If you do wrap these APIs in a try/catch, make sure you re-throw the original error so it can be caught by Next.js. -- Alternatively, insert [`unstable_noStore()`](docs/app/api-reference/functions/unstable_noStore) before the try/catch. diff --git a/packages/next/src/client/components/maybe-postpone.ts b/packages/next/src/client/components/maybe-postpone.ts index 921a15149c..18773a6f86 100644 --- a/packages/next/src/client/components/maybe-postpone.ts +++ b/packages/next/src/client/components/maybe-postpone.ts @@ -22,8 +22,8 @@ export function maybePostpone( staticGenerationStore.postponeWasTriggered = true React.unstable_postpone( - `This page needs to opt out of static rendering at this point because it used ` + - `${reason}. React throws this special object to bail out. It should not be caught ` + - `by your own try/catch. Learn more: https://nextjs.org/docs/messages/ppr-postpone-error` + `This page needs to bail out of prerendering at this point because it used ${reason}. ` + + `React throws this special object to indicate where. It should not be caught by ` + + `your own try/catch. Learn more: https://nextjs.org/docs/messages/ppr-caught-error` ) } diff --git a/packages/next/src/client/components/static-generation-bailout.ts b/packages/next/src/client/components/static-generation-bailout.ts index 473091a6a6..354ee9a162 100644 --- a/packages/next/src/client/components/static-generation-bailout.ts +++ b/packages/next/src/client/components/static-generation-bailout.ts @@ -45,7 +45,7 @@ export const staticGenerationBailout: StaticGenerationBailout = ( link: 'https://nextjs.org/docs/messages/dynamic-server-error', }) - maybePostpone(staticGenerationStore, message) + maybePostpone(staticGenerationStore, reason) // As this is a bailout, we don't want to revalidate, so set the revalidate // to 0. diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index d46d53eb25..4e1a527621 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -1029,7 +1029,10 @@ async function renderToHTMLOrFlightImpl( // as we won't be able to generate the static part warn('') error( - `Postpone signal was caught while rendering ${urlPathname}. Check to see if you're try/catching a Next.js API such as headers / cookies, or a fetch with "no-store". Learn more: https://nextjs.org/docs/messages/ppr-postpone-error` + `Prerendering ${urlPathname} needs to partially bail out because something dynamic was used. ` + + `React throws a special object to indicate where we need to bail out but it was caught ` + + `by a try/catch or a Promise was not awaited. These special objects should not be caught ` + + `by your own try/catch. Learn more: https://nextjs.org/docs/messages/ppr-caught-error` ) if (capturedErrors.length > 0) { diff --git a/test/e2e/app-dir/ppr-errors/ppr-errors.test.ts b/test/e2e/app-dir/ppr-errors/ppr-errors.test.ts index 044bd3e0e3..7099e8f0d0 100644 --- a/test/e2e/app-dir/ppr-errors/ppr-errors.test.ts +++ b/test/e2e/app-dir/ppr-errors/ppr-errors.test.ts @@ -23,14 +23,14 @@ describe('ppr build errors', () => { describe('when a postpone call was made but missing postpone data', () => { it('should fail the build', async () => { expect(stderr).toContain( - 'Postpone signal was caught while rendering /. Check to see if you\'re try/catching a Next.js API such as headers / cookies, or a fetch with "no-store".' + 'Prerendering / needs to partially bail out because something dynamic was used. ' ) }) it('should fail the build & surface any errors that were thrown by user code', async () => { // in the case of catching a postpone and throwing a new error, we log the error that the user threw to help with debugging expect(stderr).toContain( - 'Postpone signal was caught while rendering /re-throwing-error. Check to see if you\'re try/catching a Next.js API such as headers / cookies, or a fetch with "no-store".' + 'Prerendering /re-throwing-error needs to partially bail out because something dynamic was used. ' ) expect(stderr).toContain( 'The following error was thrown during build, and may help identify the source of the issue:' @@ -57,7 +57,7 @@ describe('ppr build errors', () => { describe('when a postpone call was made but missing postpone data', () => { it('should fail the build', async () => { expect(stderr).toContain( - 'Postpone signal was caught while rendering /no-suspense-boundary. Check to see if you\'re try/catching a Next.js API such as headers / cookies, or a fetch with "no-store".' + 'Prerendering /no-suspense-boundary needs to partially bail out because something dynamic was used. ' ) // the regular pre-render error should not be thrown as well, as we've already logged a more specific error @@ -69,7 +69,7 @@ describe('ppr build errors', () => { it('should fail the build & surface any errors that were thrown by user code', async () => { // in the case of catching a postpone and throwing a new error, we log the error that the user threw to help with debugging expect(stderr).toContain( - 'Postpone signal was caught while rendering /no-suspense-boundary-re-throwing-error. Check to see if you\'re try/catching a Next.js API such as headers / cookies, or a fetch with "no-store".' + 'Prerendering /no-suspense-boundary-re-throwing-error needs to partially bail out because something dynamic was used. ' ) expect(stderr).toContain( 'The following error was thrown during build, and may help identify the source of the issue:' @@ -89,7 +89,7 @@ describe('ppr build errors', () => { describe('when a postpone call is caught and logged it should', () => { it('should include a message telling why', async () => { expect(stdout).toContain( - "Logged error: This page needs to opt out of static rendering at this point because it used Page couldn't be rendered statically because it used `cookies`." + "Logged error: This page needs to bail out of prerendering at this point because it used Page couldn't be rendered statically because it used `cookies`." ) }) })