-
Notifications
You must be signed in to change notification settings - Fork 27.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: prevent router errors from being logged on the client (#71583)
Our patched `console.error` tries to skip internal router errors by checking `isNextRouterError(error)`. however, if one of those happened on the server and got replayed on the client, it gets logged differently -- it's prefixed with the `[ Server ]` badge. this changes the position where the actual error object is in `args` and thus messes up our detection, so we end up printing it out (instead of hiding it like we should) This PR adds a check that attempts to match replayed server errors (following [similar logic from react devtools](https://github.com/facebook/react/blob/65a56d0e99261481c721334a3ec4561d173594cd/packages/react-devtools-shared/src/backend/flight/renderer.js#L88-L93)) and thus also filter out router errors that originated on the server Closes #71555
- Loading branch information
1 parent
4f260d6
commit 42090c5
Showing
9 changed files
with
195 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import * as React from 'react' | ||
|
||
export default function RootLayout({ children }) { | ||
return ( | ||
<html> | ||
<head /> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} |
11 changes: 11 additions & 0 deletions
11
test/development/replayed-internal-errors/app/not-found.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import * as React from 'react' | ||
import Link from 'next/link' | ||
|
||
export default function Page() { | ||
return ( | ||
<> | ||
<h1>Not found</h1> | ||
<Link href="/">go back</Link> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import * as React from 'react' | ||
import Link from 'next/link' | ||
|
||
/** Add your relevant code here for the issue to reproduce */ | ||
export default function Home() { | ||
return ( | ||
<> | ||
<Link href="/will-redirect">Go to a page that calls redirect()</Link> | ||
<Link href="/will-notfound">Go to a page that calls notFound()</Link> | ||
</> | ||
) | ||
} |
11 changes: 11 additions & 0 deletions
11
test/development/replayed-internal-errors/app/redirect-target/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import * as React from 'react' | ||
import Link from 'next/link' | ||
|
||
export default function Page() { | ||
return ( | ||
<> | ||
<h1>Redirected</h1> | ||
<Link href="/">go back</Link> | ||
</> | ||
) | ||
} |
6 changes: 6 additions & 0 deletions
6
test/development/replayed-internal-errors/app/will-notfound/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { notFound } from 'next/navigation' | ||
|
||
export default function Page() { | ||
console.error(new Error('This error should get replayed')) | ||
notFound() // ...and this one shouldn't | ||
} |
6 changes: 6 additions & 0 deletions
6
test/development/replayed-internal-errors/app/will-redirect/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { redirect } from 'next/navigation' | ||
|
||
export default function Page() { | ||
console.error(new Error('This error should get replayed')) | ||
redirect('/redirect-target') // ...and this one shouldn't | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* eslint-env jest */ | ||
import { nextTestSetup } from 'e2e-utils' | ||
import { retry } from '../../lib/next-test-utils' | ||
|
||
describe('Replaying internal errors', () => { | ||
const { next } = nextTestSetup({ files: __dirname }) | ||
|
||
it('should not log the internal error thrown by redirect()', async () => { | ||
const EXPECTED_REPLAYED_MESSAGE = 'This error should get replayed' | ||
const OMITTED_ERROR_MESSAGE = 'NEXT_REDIRECT' | ||
|
||
const browser = await next.browser('/') | ||
|
||
await browser.elementByCss('a[href="/will-redirect"]').click() | ||
await retry(async () => { | ||
expect(await browser.elementByCss('h1').text()).toBe('Redirected') | ||
}) | ||
|
||
expect(next.cliOutput).toContain(EXPECTED_REPLAYED_MESSAGE) | ||
expect(next.cliOutput).not.toContain(OMITTED_ERROR_MESSAGE) | ||
|
||
// It'd be good to check for redbox here, | ||
// but it seems to disappear the first time we navigate to /target. | ||
// But checking console errors should be enough because they're closely tied | ||
|
||
const logs = await browser.log() | ||
|
||
expect(logs).toContainEqual( | ||
expect.objectContaining({ | ||
source: 'error', | ||
message: expect.stringContaining(EXPECTED_REPLAYED_MESSAGE), | ||
}) | ||
) | ||
|
||
expect(logs).not.toContainEqual( | ||
expect.objectContaining({ | ||
source: 'error', | ||
message: expect.stringContaining(OMITTED_ERROR_MESSAGE), | ||
}) | ||
) | ||
}) | ||
|
||
it('should not log the internal error thrown by notFound()', async () => { | ||
const EXPECTED_REPLAYED_MESSAGE = 'This error should get replayed' | ||
const OMITTED_ERROR_MESSAGE = 'NEXT_NOT_FOUND' | ||
|
||
const browser = await next.browser('/') | ||
|
||
await browser.elementByCss('a[href="/will-notfound"]').click() | ||
await retry(async () => { | ||
expect(await browser.elementByCss('h1').text()).toBe('Not found') | ||
}) | ||
|
||
expect(next.cliOutput).toContain(EXPECTED_REPLAYED_MESSAGE) | ||
expect(next.cliOutput).not.toContain(OMITTED_ERROR_MESSAGE) | ||
|
||
// It'd be good to check for redbox here, | ||
// but it seems to disappear the first time we navigate to /target. | ||
// But checking console errors should be enough because they're closely tied | ||
|
||
const logs = await browser.log() | ||
|
||
expect(logs).toContainEqual( | ||
expect.objectContaining({ | ||
source: 'error', | ||
message: expect.stringContaining(EXPECTED_REPLAYED_MESSAGE), | ||
}) | ||
) | ||
|
||
expect(logs).not.toContainEqual( | ||
expect.objectContaining({ | ||
source: 'error', | ||
message: expect.stringContaining(OMITTED_ERROR_MESSAGE), | ||
}) | ||
) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/** | ||
* @type {import('next').NextConfig} | ||
*/ | ||
const nextConfig = {} | ||
export default nextConfig |