[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-NextJS Error Boundary doesn't catch errors originating from server components. #58754

Open
1 task done
jviall opened this issue Nov 22, 2023 · 7 comments
Open
1 task done
Labels
bug Issue was opened via the bug report template.

Comments

@jviall
Copy link
jviall commented Nov 22, 2023

Link to the code that reproduces this issue

https://codesandbox.io/p/devbox/nextjs-error-boundary-catches-server-throws-mmvlsd

To Reproduce

  • Create your own ErrorBoundary component
  • Render a Server component which throws as a child of a <ErrorBoundary /> with some arbitrary fallback UI
  • run the dev server

Current vs. Expected behavior

Current Behavior:
any custom <ErrorBoundary /> component will not catch an error that is thrown from a descendent server component, instead it will be uncaught, unless a error.tsx file is defined somewhere for the given route.

Expected Behavior:
<ErrorBoundary />-like components should catch server errors without needing to be implemented as Route Segment error.tsx files.

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Sun Aug  6 20:05:33 UTC 2023
Binaries:
  Node: 20.9.0
  npm: 10.1.0
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 14.0.4-canary.9
  eslint-config-next: 14.0.2
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

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

App Router

Additional context

In the linked sandbox, adding "use client"; to the <BadComponent /> resolves the issue, proving the gap in functionality between Next's proprietary error boundary and how "native" error boundaries behave. I also don't believe this discrepancy has always been the case.

@jviall jviall added the bug Issue was opened via the bug report template. label Nov 22, 2023
@jviall
Copy link
Author
jviall commented Nov 27, 2023

The Legacy react docs mention that Server Rendering errors aren't caught by error boundaries, but this note was removed in the new React docs--has this changed?

Note
Error boundaries do not catch errors for:
Event handlers (learn more)
Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
Server side rendering
Errors thrown in the error boundary itself (rather than its children)

https://legacy.reactjs.org/docs/error-boundaries.html

@jviall
Copy link
Author
jviall commented Nov 27, 2023

This discussion topic seems to target the same issue #53618

@EvHaus
Copy link
Contributor
EvHaus commented Dec 3, 2023

Using App Router -- currently it's possible to catch server-side errors like this:

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

const ThisWillFail = async () => {
	// If this request fails I want the <ErrorBoundary> to catch it, rather than having
	// the whole page fails and fallback to `error.tsx`.
	const data = fetch('https://somepage.com/api');
	return <div>Hello</div>;
}

const Thing = () => (
	<ErrorBoundary fallback={<div>An error happened</div>}>
		<Suspense fallback={<div>Loading...</div>}>
			<ThisWillFail />
		</Suspense>
	</ErrorBoundary>
);

export default Thing;

However, if I want to display the actual error message to the user (ie. use the FallbackComponent prop instead of the fallback prop on <ErrorBoundary>) there are some problems. I want this:

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

const ThisWillFail = async () => {
	// If this request fails I want the <ErrorBoundary> to catch it, rather than having
	// the whole page fails and fallback to `error.tsx`.
	const data = fetch('https://somepage.com/api');
	return <div>Hello</div>;
}

const ErrorHandler = ({error}) => {
	return <div>{error.message}</div>
}

const Thing = () => (
	<ErrorBoundary FallbackComponent={ErrorHandler}>
		<Suspense fallback={<div>Loading...</div>}>
			<ThisWillFail />
		</Suspense>
	</ErrorBoundary>
);

export default Thing;
  1. Firstly, adding FallbackComponent causes this failure in Next.js: Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server"..
  2. I can get around that error by putting use client at the top of the file. But that causes the whole app to go into an infinite rendering loop where (for some reason) the error is caught, but then the whole app re-renders, fails again, gets caught, gets re-rendered, etc...

The Next.js Error Handling documentation mentions using ErrorBoundary but doesn't provide any examples of how to do it.

Any help would be appreciated here.

@exelimpichment
Copy link
  1. I can get around that error by putting use client at the top of the file. But that causes the whole app to go into an infinite rendering loop where (for some reason) the error is caught, but then the whole app re-renders, fails again, gets caught, gets re-rendered, etc...

looks like no infinite loops for now.
works form me fine the way you described

@th91vi
Copy link
th91vi commented Jun 12, 2024

@EvHaus thanks a lot for your answer. Your proposition is working for me on Next 14.1.4

Can you share more details on why ErrorBoundary and Suspense must be o the same component level to make it work?

Previously I was trying to use my ErrorBoundary 1 level below my Suspense and it was not working.

Don't know if I am an edge case, but it's very frustrating that Vercel provides poor documentation about how to handle RSC errors.

@EvHaus
Copy link
Contributor
EvHaus commented Jun 12, 2024

@th91vi Sorry, I don't know. That's why this issue exists. :) Need Next.js devs to help us.

@zecka
Copy link
zecka commented Jun 28, 2024

@EvHaus
Note that using <Suspense> here is probably not the desired behavior for most people looking to catch errors in server components.

Correct me if I'm wrong, but don't forget that suspense will shift part of the server rendering to a second time and display a fallback to the client in the meantime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests

5 participants