create-react-app: Make it clearer in error overlay when error boundary has caught an error

This issue was discussed in #3627, but it’s been marked and stale as locked so I’m opening another issue.

As discussed in #3627, create-react-app will always display an error overlay for errors in development mode, even when the error is caught via an error boundary. This confused me at first, and it’s also confusing users of the routing library I maintain. This library throws a NotFoundError when it can’t match a URL segment, and uses a <NotFoundBoundary> component to allow the user to render a 404 page.

<div className='App'>
  <Sidebar />
  <main>
    <NotFoundBoundary render={() => <NotFoundPage />}>
      {/**
      <View /> throws a NotFoundError if they can't find a route any content for
      the part of the URL they correspond to. They can be nested, so even if
      this view doesn't throw, a child view might -- thus boundaries are 👌
      **/}
      <View />
    </NotFoundBoundary>
  </main>
</div>

I feel like the best approach would be to not show error overlays for caught errors (Dan mentioned it’s on his todos). But just making it clearer that the error was caught would be a big improvement.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 24
  • Comments: 24 (14 by maintainers)

Commits related to this issue

Most upvoted comments

Let me share some background on why it works this way.

Yes, error boundaries are a way to catch errors. However, error boundaries are a part of your UI. This means that, just like any part of UI, they might not be visible. Maybe they’re inside a display: none tree. Maybe they’re behind something else due to the z-index. Maybe the part that errored is below or above the viewport. Maybe it is in a portal (e.g. a tooltip). Maybe an error boundary wraps a component that currently has very little height and width, and is very easy to miss. As a result of all these plausible scenarios, you risk not noticing the error at all.

Next, I will claim that most errors are, in fact, going to be programming errors, and not intentional ones. People make mistakes all the time, and JavaScript is a dynamic language. There will be typos, null references, crashes, and so on. During the course of development you’ll have a lot more of them than intentional throws. I think we should optimize the experience for catching bad errors early, even if it hurts some other cases.

Additionally, the distinction between “caught” and “uncaught” errors in this case doesn’t have any substance. It would appear that even wrapping your whole app in one boundary would suddenly mark all errors as “caught”. Since every app should have a boundary, and we’ll likely add one by default into CRA and Next templates to encourage people to design them, there would be no “uncaught” errors in principle. So it is not a very meaningful distinction for rendering errors.

Due to these three points, I recommend against a blanket solution like “don’t show dialog for caught errors”. In practice, most of them should be “caught” so this is more or less equivalent to removing the error dialog altogether and hoping for the best.

I think it’s fair to say that like in the original post, some errors are intentional. We don’t have a very good mechanism for distinguishing them in React. We might eventually introduce one. But these are already shown in console, so it’s also not like the dialog acts in a special way. It attempts to mirror what the console is doing.

I get that the dialog is obtrusive in these cases. As a compromise, I propose the following solution:

  1. For ReferenceError or TypeError, always show the dialog. No exemptions.
  2. If possible, filter out browser-generated network errors and never show them in a dialog.
  3. For other types of error, check if the error was caught by a boundary. If it was caught by a boundary, do not show the dialog but show the error marker in the corner (like in https://github.com/facebook/create-react-app/issues/6530#issuecomment-661427922). Clicking the error marker shows the dialog. The error marker should have a small clear button so you can dismiss it without making the dialog appear. In case the error is expected.

This way, you’re not going to miss important errors, but the dialog also doesn’t distract you from seeing your own custom boundaries.

I’d like to bring this up again. Especially as ErrorBoundaries are increasing in use, having the error-overlay pop up when the error is handled is pretty disruptive. Would love for caught errors to not trigger the overlay.

Why is the CRA equivalent of a blue screen of death a good idea when people are doing good things and placing error boundaries so that their users don’t crash their entire application? That’s signalling to the developer that they didn’t just have an error, but they screwed up their entire error handling.

On the other hand, if the developer properly implements an error boundary that catches an error - great, now that error boundary can show a cool UI with stack trace and help them debug and such. Why do we have to forcefully crash the entire application when that debugging information has an explicit place in the application that the developer specified? Why not just make it easy to have an error boundary that - in development - gives the same cool stack trace information that this overlay has?

Why make compromises and prompt people to want to completely disable this incredibly valuable and useful feature? This is the same thinking that resulted in Windows ME. Of course, Microsoft knows best so they need to bombard you with messages so much you ignore them all and then want to throw your computer out the window.

I do like “For ReferenceError or TypeError, always show the dialog. No exemptions.” though. 100% those are always real errors. But seriously, Networking Error does not mean my code is buggy.

hey I’m late for the party, but how about an option to swallow the error in error boundary?

componentDidCatch(error, errorInfo) {
  report(error, errorInfo);
  errorInfo.hasBeenCaught(); // or .catch(), .stopPropogation(), .markAsHandled(), etc

  // or
  return true; // doesn't feel explicit enough
 }

(I’d go with .catch() though it might be confusing with promises)

In my app we are allowing user code in some of the places, so showing the error overlay in spite of the boundary is useful to us. I’m also having the opposite case, where react errors are not shown in Error Overlay, but I am using a custom error overlay, it’s probably related to that.

At the very least, giving the overlay iframe a class name so we can target it with css would need enough for opt-out-ability…

I think that would also be nice, but its presence is really annoying when working on error boundaries or if you’re working on one part of the app but another part of the app has a failing network request but doesn’t really matter because you’re not working on that right now, etc.

An error boundary is a way to say: “this is handled” Having something jump out at you when you’ve handled something is annoying and unhelpful IMO.

I’m not convinced it’d be a DX win to see no overlay whatsoever for an unhandled error in your application.

I agree with you. I’m not talking about “unhandled errors” we’re talking about handled errors. Errors handled by an error boundary. That’s handling the error.

Tip: If you delete the error stack trace, the dev error overlay will not show (at least in next.js)

👉 delete error.stack

We are doing that in Blitz for any errors coming from the server

I’m not convinced it’d be a DX win to see no overlay whatsoever for an unhandled error in your application. I guess this could become more annoying if you’re leveraging error boundaries for “known” view states instead of unexpected errors.

I definitely do feel pain for network errors—in this case, the error overlay could remain dismissed if more errors continue to flow in:

I would also like for there to be some option to not show an overlay in specific cases of an error boundary. It should definitely be explicit; so something should be called in order to mark the error as “handled” such that it shouldn’t show an overlay.

A good analogy would be like exceptions: if an exception is caught, you wouldn’t implicitly show a big error saying that an exception was thrown.

A use case of this right now is that I have a JSON editor where you can basically write the props a component would receive directly in the form of a JSON string. The resulting component will fail more often than not due to invalid props being passed, and we can’t know ahead of time if the component is going to fail due to bad props. The error overlay popping up in this case is annoying to say the least, since for every character typed into the JSON editor, the error overlay will keep popping up over and over again and you have to dismiss it over and over again for each character you type. This is why I would like to be able to “expect” that exceptions get thrown in the component, and that I can handle the exception 100% in my own way e.g. showing a failure message inline, and - during development - show an error stack trace too, but inline, not as an overlay.

intentional error boundaries and accidental ones.

I’d say including an error boundary at all is intentional. I think the developer will see that their error boundary was triggered and can investigate. I suppose it may be unfortunate that they would not get the overlay to help them investigate.

What if we do this… Add a class or ID to the iframe so it can be targeted with CSS to add display: none;? That’s as good as any opt-out I can think of. Thoughts?

Agree with @ntucker here… network 404’s are perfectly reasonable responses depending on the UI. Any domain model that’s built from the top down will get 404’s when querying down the domain model…

Very frustrating to hit X every time… and very frustrating to explain to a non-tech person why were seeing giant “this is broken” errors when they pop-in for a quick look @ something.

#9342 doesn’t close this original issue (which I think should be addressed still), but it does make it easier for folks to hide the overlay if they don’t want it to show up for their app.