You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Developer should throw custom exception in interceptor #1950
When i write a interceptor class extends Interceptor, in some case, i wish throw some exception to tell the developer some thing went wrong. so i throw a custom exception in override function 'onResponse', but this exception was catched by DioException, and this exception can not tell me more infomation about this error.
So, i wish this will be supported
Solution Brainstorm
No response
Reacted by ykaito21, Zensonaton, Jagmit Singh, Bao Huynh, ZZ and Radomir Epur
Adding request information to the exception is one thing. Figuring out a sensible way to convert arbitrary exceptions/objects into strings is another. Any ideas for this? We call toString() on the inner error but we don't always pass a message to the DioException - that is why it says [unknown]: null.
Based on the above comments, the root cause of the exception is the JSON fails to be decoded before it reaches your exception, so a FormatException was thrown. Otherwise, it would be DioException(APIException).
it's my wrong, i show a wrong example.
this is a good example i think for my issue.
when i get a response with code 401, i throw a apiexception, in the terminal, i see which i throwed apiexception, but it wrapped with dioException, so i can't not catch this error exactly next time.
It would be great if the custom error thrown in the interceptor could be caught in the onError of Future.then(onData, onError), but currently, I don't know why it can't be caught. I'm not very familiar with Dart yet and don't know how to handle it.
import'package:dio/src/dio/dio_for_native.dart';
finalclassCustomExceptionDioextendsDioForNative {
CustomExceptionDio([super.baseOptions]);
@overrideFuture<Response<T>> fetch<T>(RequestOptions requestOptions) async {
try {
returnawaitsuper.fetch<T>(requestOptions);
} catch (e) {
if (e isDioException) {
final customException = e.error;
if (customException !=null) throw customException;
}
rethrow;
}
}
}
Then, instead of Dio() constructor use CustomExceptionDio().
P. S. Don't forget that a instance of DioForNative won't work on web. For web you'll need to do the same trick with DioForBrowser, then use the conditional import to decide which instance to create depending on the platform you're program runs on(exactly like Dio() constructor does under the hood)
Interceptor example:
finalclassExceptionMapperInterceptorextendsInterceptor {
constExceptionMapperInterceptor();
@overridevoidonError(DioException err, ErrorInterceptorHandler handler) {
final statusCode = err.response?.statusCode;
if (statusCode !=null&& statusCode >=HttpStatus.internalServerError) {
return handler.reject(
DioException(
error:MyCustomException(), // <- Here you pass your exception
requestOptions: err.requestOptions,
),
);
}
handler.next(err);
}
}
Add a way to throw a custom exception from inside an Interceptor and have the caller receive the original error rather than an opaque DioException. Initial API shape: a DioException.custom(error, {stackTrace, response, requestOptions}) factory whose wrapped error is accessible via DioException.error (already exists on the class).
Update the interceptor error path so that when a user throws a non-DioException from onRequest / onResponse / onError, it's wrapped into DioException.custom(...) instead of being swallowed into a generic DioException with no preserved original.
Add tests for all three interceptor callback throw paths, verifying the caller can recover the original exception type.
Happy to defer if someone's already on this, or adjust the API shape (e.g., a new property on DioException rather than a .custom(...) factory, or a different name) if you'd prefer.
Happy to defer if someone's already on this, or adjust the API shape (e.g., a new property on DioException rather than a .custom(...) factory, or a different name) if you'd prefer.
@realmeylisdev (with AI) seems to be working on it, but the PR got closed due to the repo being deleted.
Activity
ueman commentedon Sep 1, 2023
This makes a lot of sense.
I think we either should properly wrap the exception, or we shouldn't wrap it at all.
kuhnroyal commentedon Sep 1, 2023
I think we do.
Just from looking at the screens, the reported exception probably originated at the
jsonDecode.AlexV525 commentedon Sep 15, 2023
These (#1949) are quite similar which requires a nicer first-look regarding the exception.
kuhnroyal commentedon Sep 15, 2023
Adding request information to the exception is one thing. Figuring out a sensible way to convert arbitrary exceptions/objects into strings is another. Any ideas for this? We call
toString()on the inner error but we don't always pass a message to theDioException- that is why it says[unknown]: null.brian030128 commentedon Sep 30, 2023
DioExceptionhas a error field, this is probably what you're looking for?dio/dio/lib/src/dio_exception.dart
Lines 176 to 178 in 4d7ea68
AlexV525 commentedon Oct 14, 2023
Based on the above comments, the root cause of the exception is the JSON fails to be decoded before it reaches your exception, so a
FormatExceptionwas thrown. Otherwise, it would beDioException(APIException).Closing as it's working as intended.
WutangNailao commentedon Oct 21, 2023
it's my wrong, i show a wrong example.


this is a good example i think for my issue.
when i get a response with code 401, i throw a apiexception, in the terminal, i see which i throwed apiexception, but it wrapped with dioException, so i can't not catch this error exactly next time.
kuhnroyal commentedon Nov 3, 2023
Not sure how we can achieve that other than the developer registering custom types somewhere.
trongtindev commentedon Jan 18, 2025
Is there any temporary solution? :(
ZhZhZhen commentedon Feb 16, 2025
It would be great if the custom error thrown in the interceptor could be caught in the
onErrorofFuture.then(onData, onError), but currently, I don't know why it can't be caught. I'm not very familiar with Dart yet and don't know how to handle it.kuhnroyal commentedon Feb 26, 2025
Maybe we can add something like
DioException.custom(MyCustomError())and unwrap this later?radomir9720 commentedon May 19, 2025
Sounds like a solution, since there are no other proposals. Waiting for this feature.
radomir9720 commentedon May 19, 2025
There is. For example, a straightforward one:
Then, instead of
Dio()constructor useCustomExceptionDio().P. S. Don't forget that a instance of
DioForNativewon't work on web. For web you'll need to do the same trick withDioForBrowser, then use the conditional import to decide which instance to create depending on the platform you're program runs on(exactly likeDio()constructor does under the hood)Interceptor example:
theprantadutta commentedon May 12, 2026
Hi @kuhnroyal @AlexV525 — would it be useful if I took this on? My plan, based on @kuhnroyal's sketch upthread:
Interceptorand have the caller receive the original error rather than an opaqueDioException. Initial API shape: aDioException.custom(error, {stackTrace, response, requestOptions})factory whose wrapped error is accessible viaDioException.error(already exists on the class).DioExceptionfromonRequest/onResponse/onError, it's wrapped intoDioException.custom(...)instead of being swallowed into a genericDioExceptionwith no preserved original.Happy to defer if someone's already on this, or adjust the API shape (e.g., a new property on
DioExceptionrather than a.custom(...)factory, or a different name) if you'd prefer.AlexV525 commentedon May 14, 2026
@realmeylisdev (with AI) seems to be working on it, but the PR got closed due to the repo being deleted.
realmeylisdev commentedon May 14, 2026
Picked this back up — opened #2514 with the design previously sketched by @kuhnroyal here (#1950 (comment)) and prototyped in the closed #2484.
Scope of #2514:
DioException.custom(...)factory +propagateInnerErrorflag (defaultfalse).rejectCustomError(...)helper onRequestInterceptorHandler,ResponseInterceptorHandler,ErrorInterceptorHandler.DioMixin.fetch()usingError.throwWithStackTrace.on DioException catch (e)keeps working unchanged (verified by a regression sentinel test).Happy to iterate on naming or anything else during review.
theprantadutta commentedon May 14, 2026
Thanks @AlexV525 for the heads-up, and great to see you back on this @realmeylisdev — all yours. I'll bow out cleanly. Good luck with #2514!
test(dio): cover async DioException.custom propagation (cfug#1950)
refactor(dio): rename to rejectCustom/isCustom per review; restore cf…