sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.39k stars 4.1k forks source link

Feature suggestion: error handling API #1096

Open jbmoelker opened 6 years ago

jbmoelker commented 6 years ago

This is a feature suggestion for adding an error handling API to Svelte components based on this remark.

There is currently no built-in way to handle errors in Svelte components. React 16 introduced the concept of Error Boundaries to standardise this:

// Error handling life cycle in React:
componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

I imagine a similar concept would be a good addition to Svelte components. Maybe

declaratively, as event on element:

<MyComponent on:error='doSomethingWith(e)' />

imperatively, as component life cycle event:

oncreate() { /* ... */ },
onerror(e) { /* ... */ },

I would check if the error is handled inside the component - with the onerror handler - first, and only if it isn't handled there, have it bubble up and trigger on:error. If a developer wants to handle the error inside the component and also wants to have it bubble up, they can use this.fire('error', e) within onerror.

98mux commented 2 years ago

@basaran @rster2002 This is probably more svelte like:

<script>
    import ErrorComponent from "./ErrorComponent.svelte";

    let a;
    let b = {};

    a.b; // Error is thrown here

    b.c = true;
</script>

<svelte:error on:error={(error) => {}} let:error={error}>
  <ErrorComponent statusCode={error.statusCode}/>
</svelte:error>

{#if b.c}
    Property `c` on `b` is true
{/if}

One idea is to ignore the error when returning false on the on:error

Seems like this guy has the same idea: https://github.com/sveltejs/rfcs/pull/46#issuecomment-873324487

tv42 commented 2 years ago

I think it's worth mentioning that SvelteKit seems to be solving this problem one layer higher, with a separate src/routes/**/__error.svelte page: https://kit.svelte.dev/docs#layouts-error-pages

mlandisbqs commented 2 years ago

I'm at a complete loss with this issue. We are preparing our svelte 3 application for a production release and encounter the following scenario:

^-- this works gangbusters, except...

I noticed a scenario where a component on a particular page references a property we just removed from the session store ($session.user.email). Before the page route is redirected, the component on the current page throws

 TypeError: Cannot read properties of undefined (reading 'email')

...this appears to halt all execution and silently crashes the page, instead of redirecting the page route. I can see the errors in the console.

I can fix this specific scenario easily with a

$session.user?.email

...however there is no way for me to prevent the next guy from breaking this with the next code change unless we exercise session expiration on every page when we regression test the app. See the problem?

Note: We have no issues handling specific try catch scenarios that the code is anticipating. But there must be a way to implement a global error handler that I can wire up to my existing comprehensive error store + component for these kind of issues. I have tried Boundary and ErrorBoundary solutions as they have been floated in this thread, but none of those invoke the handler under the scenario described above when I tested them out.

IMHO we need a solution analogous to a global-scoped exception filter in NestJS (https://docs.nestjs.com/exception-filters) - a safety net for unanticipated errors that we can hook into and handle in our own preferred way without halting client-side app execution.

hyrious commented 2 years ago

Just want to add an example demonstrating the issue here: https://svelte.dev/repl/990219d2c7a34ce59bd7697ff552ea8a?version=3.46.4

In short, $: throw will shutdown the whole svelte app. Because these reactive expressions will be compiled to comp.$$.update() and then be called in flush() directly without a try-catch. The flush() is so fragile that if it is broken at the middle, it will never set update_scheduled to true, then there will be no flush() be called.

gabrielnvian commented 1 year ago

Hey everyone, is there any progress on this? It is the last major feature I would need before shipping my app to production

benbucksch commented 1 year ago

Errors can happen (at least) in the following places:

If I missed some error classes, please add them.

For many of these error classes, the developer can manually add a try/catch, for each instance. However, for a large app, that can quickly become so cumbersome to not be realistic. And it also goes against the philosophy of Svelte of writing natural, concise code that expresses clearly what it does, not mechanical boilerplate. ("No boilerplate" is one of the Svelte mantras.)

Just to quantify the magnitude of the problem: Our app currently has almost 800 on:foo event handlers and over 300 $: reactivity statements. We now have a wrapper function to catch errors in event handlers, but even with that, it's barely realistic to change all this code and add the extra boilerplate everywhere. And if we did, we would then run into other Svelte issues.

JS, Java, C#, C++ and many other languages have exceptions, which bubble up errors the function stack and allow to catch and handle them on any level. We need the same on the UI level, to catch and bubble up errors to container Svelte components. Error boundaries similar to React seem like a good concept for that. It allows me to write code to handle the error - either replace the child component, or display the error.

Please add this to Svelte. Solid error handling is necessary for a reliable application in production.

FeldrinH commented 1 year ago

Hey everyone, is there any progress on this?

This comment on the Error Boundary RFC indicates that some kind of error boundaries are (probably) coming in Svelte 5.