Using dio in a Flutter application, having "Uncaught exceptions" enabled in the debugger results in breaking within the dio library when an HTTP call fails, even though at the application level that exception is caught.
If the "caughtness" of the exception can't be detected due to framework abstractions, something you might do in node is configure "skipFiles": [ "**/.pub-cache/**" ] on the launch config, but there doesn't seem to be an equivalent option for dart launches.
Even worse, if "Debug my code" / "Debug my code + packages" is set to "Debug my code", execution still breaks inside the dio package and pauses execution, but the editor doesn't open the file and show the break -- vscode only very subtly changes visual state.
Disabling uncaught exceptions constantly is my team's current workaround, but it's on by default and seems to occasionally re-enable itself, and it would be a useful good feature to be able to leave on. This is a frequent failure point when using Flutter driver tests with both the app and the tests running within vscode, because every test that results in the app getting a non-200 HTTP status code pauses execution often invisibly and times out the driver tests. The application level catches that exception and displays error UI to the user and that's what we're trying to test, but vscode in a default config of "[x] uncaught exceptions" and "debug my code" will appear to just freeze your app and the only indication is that the debugger toolbar just flipped from pause to play
I could set up a fresh Flutter app and try building a reproducible test case if that's not enough information to connect the experience with known issues
As for workarounds, is there any way at the workspace or launch config level to disable breaking on anything under .pub-cache like skipFiles does for the node debugger?
This is fairly similar to #2670.
Using
dioin a Flutter application, having "Uncaught exceptions" enabled in the debugger results in breaking within the dio library when an HTTP call fails, even though at the application level that exception is caught.
I believe the reason this is occurring is because dio is using .catchError() and not a normal catch block. This means the VM is unable to tell that the exception will be handled. My understanding - though I don't work on the VM - is that at the time an exception is thrown, the VM examines the stack looking for catch blocks to figure out if the exception will be caught, and it can't reliably detect the use of .catchError because they can be chained on outside of what's in the stack.
I think the best fix for that specific case may be for dio to use try/catch (see https://github.com/Dart-Code/Dart-Code/issues/2670#issuecomment-668535375).
Even worse, if "Debug my code" / "Debug my code + packages" is set to "Debug my code", execution still breaks inside the dio package and pauses execution, but the editor doesn't open the file and show the break -- vscode only very subtly changes visual state.
This is a bug I've fixed today (#2699). I'm planning to make an alpha build later today that you could try, which should avoid this issue (it will just focus the top frame of the stack if there is no "use code" in the stack).
Disabling uncaught exceptions constantly is my team's current workaround, but it's on by default and seems to occasionally re-enable itself, and it would be a useful good feature to be able to leave on.
Tbh, between the mis-firing with .catchError() and Flutter already catching most exceptions in the framework anyway, I'm less sure that this functionality is useful enough to be enabled by default than I have been in the past. I think right now it's generally better to untick it and just enable it when you think it'd be useful (though I wish this wasn't the case).
As for workarounds, is there any way at the workspace or launch config level to disable breaking on anything under
.pub-cachelikeskipFilesdoes for the node debugger?
There isn't currently anything like this - though it might not be a bad idea. Some workarounds have been discussed in #2699 but my preference is always to try and fix the issues before working around (because otherwise they'll never be addressed - the workaround will just become the normal way).
Of course, I'd prefer users not need to know that when they set "Debug just my code" they should probably also add some specific skipFiles, so we might want to just do this based on the setting (maybe with a setting to disable it). I would be concerned about accidentally making it so exceptions are just silently lost (eg. they're thrown in a package, but don't bubble up, so you just never see them).
I'll think about this a little more and maybe play with some ideas to see how they feel. Thanks!
There's a preview build here that should have the fix above for focusing a stack frame (and showing a red exception popup) even when there's no user code in the stack:
https://github.com/Dart-Code/Dart-Code/releases/tag/v3.14.0-alpha.1
I would be concerned about accidentally making it so exceptions are just silently lost (eg. they're thrown in a package, but don't bubble up, so you just never see them).
Could a potential mitigation there be rendering them to the Debug Console with some visual style?
Of course, I'd prefer users not need to know that when they set "Debug just my code" they should probably also add some specific skipFiles, so we might want to just do this based on the setting (maybe with a setting to disable it). I would be concerned about accidentally making it so exceptions are just silently lost (eg. they're thrown in a package, but don't bubble up, so you just never see them).
I like this idea
Could another improvement be that the "Debug just my code" setting is ignored if the debugger ends up breaking inside package code? If execution is going to get stopped on a line, it doesn't seem like it would ever be desirable for that to not be surfaced as an editor opening and highlighting the paused line. That preference seems only helpful to whatever extent it can prevent the breakpoint
Could a potential mitigation there be rendering them to the Debug Console with some visual style?
Sure, but it feels like a step in the opposite direction - in order to prevent breaking on exceptions we don't want, we'd be stopping breaking on exceptions we do want. You can easily achieve that today by just unticking the debug options, and it's more understandable what's happening.
Here's an example of the issue:
void main() {
final a = <String>[];
print(a[1]);
}

If we had implemented automatic resuming for SDK libraries because I'm on "Debug my code", this exception (which occurred inside List) would've been resumed. I don't think that's what anybody would expect - as far as the user is concerned, the exception really occurred at the location shown here (which is the second stack frame).
Could another improvement be that the "Debug just my code" setting is ignored if the debugger ends up breaking inside package code? If execution is going to get stopped on a line, it doesn't seem like it would ever be desirable for that to not be surfaced as an editor opening and highlighting the paused line.
Right, but that's not the only possibility - the exception could occur in package code but there might be user code in the stack - the screenshot above is a perfect example - we moved the focus to the first stack frame that is user code, which is the most appropriate place here - the error is on the highlighted line, not inside the List class.
But I agree it's never desirable not to focus somewhere and that's what #2699 fixed (which is in the preview linked above). If there is no user code in the stack, then we'll allow the top-most frame to be focused still. This should avoid ever breaking without jumping to code and showing a red exception popup.
Thinking about this more - I don't think fixing this with any sort of skip is going to work. The same issue can happen entirely within your own code:
Future<void> main() async {
await throwErr().catchError((e) => print('Caught!'));
}
Future<void> throwErr() async {
throw 'a';
}
Logically, there's no uncaught exception here. Yet the debugger will break with only "uncaught exceptions" ticked.
So now I'm (again) leaning towards this being something that should be fixed in the packages. Rewriting the code to use a real catch (for ex. in dio) would fix the issue in a way that may have no compromises. If you need to debug code that is using catchError, I think unticking the exceptions tickboxes makes the most sense. Anything else feels like a bad workaround that introduces more inconsistencies.
I filed an issue about a lint for catchError() here:
https://github.com/dart-lang/linter/issues/2212
I think that's the best way to solve most of the issues here. Having a lint would make it easier when fixing up packages/sending PRs to ensure .catchError() is not added back. I believe it would address the issue without any compromises (though it does require existing code/packages using catchError to be updated).
I don't think there's really much to do here in the extension. I think the real issue is https://github.com/dart-lang/sdk/issues/37953 (which to be improved, would need to be done in the SDK - I'll see if I can start a discussion about this somewhere), and the current best workaround is using try/catch where possible (which I see there's an open issue for in dio). I don't think something like skipFiles solves the issue properly (see https://github.com/Dart-Code/Dart-Code/issues/2701#issuecomment-669787405).
Does that sound fair?
Yeah :'(
The dio crew has been asleep at the wheel on this though: https://github.com/flutterchina/dio/issues/887
Anyone here interested in pushing them a PR?