dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.04k stars 1.55k forks source link

Dart lacks a way to create an error chain #56249

Open rrousselGit opened 1 month ago

rrousselGit commented 1 month ago

Hello!

One common use-case when handling error is to transform an error to add more context around it. This is often done by caching an exception, and throwing a new one.

The problem is twofold:

One way to deal with that is to include the error/stacktrace of the original error in the toString of the new error. But that's not perfect. For instance, it can easily confuse error reporting libraries. And debugging tools may have difficulties rendering the error in a readable way

One solution to that in Javascript is the cause field on errors:

throw new Error('message', {cause: someOtherError})

We could do the same with Dart's Error/Exception (maybe with an extra field for the StackTrace):

throw Exception('message', cause: Cause(error, stackTrace));
dart-github-bot commented 1 month ago

Summary: Dart currently lacks a mechanism to create error chains, making it difficult to preserve original error information and stack traces when re-throwing errors. This leads to loss of context and potential confusion for debugging tools and error reporting libraries.

lrhn commented 1 month ago

This sounds like something that should apply to exceptions, not errors. You shouldn't handle or transform an Error, because they shouldn't be happening at all. And if you do, it should be because someone higher up will want to catch and handle it, which means it should be rehtrown as an Exception, not an Error.

It may make sense to give Exception a cause (which may be an Error). It probably means having a default toString which prints the chain of stack traces. Maybe avoid common suffixes in the stack traces, like Java does. More work, but you shouldn't be doing toString on Exception to begin with, except for debugging - you should handle the problem instead.

rrousselGit commented 1 month ago

I'm not sure why this would be unique to exceptions. We could convert an Exception into an Error, and include a cause in the error.

For example, a function that takes a JSON as String could convert a FormatException into an ArgumentError, and pass the FormatException as cause to that ArgumentError.

void function(String json) {
  try {
    jsonCode(json);
  } on FormatException catch (e, s) {
    throw ArgumentError('json is not a valid JSON', cause: Cause(e, s));
  }
}
lrhn commented 1 month ago

Handling an exception by throwing an error ... is a reasonable use-case.