❔ Exception Handling
I wonder whether I shall implement differentiated exception handling with detailed custom exceptions such as
DataRecordNotFoundException
only for displaying a more detailed error message in the UI, besides the error message in UI the handling would be the same for all exceptions. Is that reason enough tho? I'm sure the user would be thankful to know as detailed as possible what went wrong, but at the end of the day it wouldn't really change how he interacts with my app, my concern is just about being as transparent as possible what went wrong 🤷♂️ . What do you guys think?60 Replies
The exception type is information for the developer
The message inside it can sometimes be shown to the user, if you know it's safe
I know that
Well, it answers the question you asked in your thread.
So... if you already know it... why ask? 🙂
my question is whether slightly more detailed error message in UI is reason enough for doing diferentiated exception handling with different exception types and messages for different scenarios. How did you answer that?
If the only reason for adding new exceptions is to provide a more detailed error to the user, the new exception types are not needed. They don't make your program behave differently.
Exceptions are for exceptional things etc
Can the user reasonably act on the messages they get shown?
okay but what about Update method where no record with matching PK value can be found?
I was gonna do something like
throw new DataRecordNotFoundException("the record to update does not exist")
so that caller can then display that messageIs this an API or a desktop app?
throwing other existing exception type with this message doesn't match, which one would I do?
MAUI
with xaml, not with blazor
I'd personally not throw exceptions if an update failed because of missing record, I'd handle that as an expected error - but sure, that information is safe to show.. but probably with less technical speak
what does handle as an expectred error mean?
how handle it?
so, exceptions are these magical things that can travel up the call-stack right?
we dont wanna handle here within Update method. We want caller / viewmodel to handle this by dispalying X or displaying Y. Hence I throw
ye
Doing that to update a UI is... using exceptions as control flow and thats bad
I'd rather have your
Update(...)
method return a result that says if it succeded or not
thats what I mean with "expected error"
if we can expect the record to not exist, we should let a caller of our method know that "hey, sometimes when you call me, I will fail and I show that this way"okay so. Should do something like:
or is there some commonly used lib or smth to use instead of defining my own simple type like that?
yeah that would be a pretty common way to do that
so I define my own type okay
if you want, sure
There is
OneOf
, a popular library that lets you specify very strictly what types of errors can happenso what do you personally use? your own type or lib?
I personally quite like
Remora.Results
as an in-between, it doesnt expose error types, but it has strictly typed errors
Remora is a pretty small and lightweight result object
it has an abstract base error that all errors inherit from, and some implicits to make it easy to work with
a method that is declared to return Result<T>
can safely just return someT;
and it just works. Same with errors
return new RecordNotFound(...);
"just works"this is actually really nice
this is how error handling is done in languages without exceptions btw
I can even use this for multiple things, say my repos and my typed http clients
Go uses this, Rust uses this
thx
etc
yes!
absolutely
its a fantastic pattern to use when things "can fail" and you need to communicate that
mhm but one concern
yup?
I thought this would be really nice cuz we managed to replace try catches with if elses. But here is the thing, we might still run into exceptional issues, so try catch is still needed for things like I/O ain't it?
sure, yep
which means we end up doign both try catch AND if else
which makes me not a fan of Result type
or do I missunderstand
they are for different things thou, if you ask me
Results are for business failures
exceptions are for technical failures
I see
a database connection broke because the user unplugged their internet while it was querying, thats not something your program can fix, so thats an exception
if you can find a place to add a try-catch to handle it so your program doesnt enter a fail-state, then do so
but having a global "catch all" exception handler can be dangerous
you might catch the exception and ignore it, but what if your program is now in a bad state
like, an operation stopped half-way through and now some of your variables are "out of sync"
oh wait I think I can answer myself why Result type is still very nice. Let me try to answer:
We can handle exceptions in our Repository layer, Fill our Result type with exception's message so we handle both exceptional and unexceptional issues with the Result type and the caller would only ever have to use If else, no more try catches 🙂
so only one try catch in repo but not all the calling stack
yep, we can do that!
sure. If you catch any and all exceptions related to the database query in your repo, the repo can safely return the result or an
ExceptionError(ex)
not the worlds nicest thing, and only do this for things like IO where you know its still fine to continuewhy not the nicest thing?
well because "ExceptionError" isnt really a business thing 🙂
the failure itself can be, but the details about it are... unexpected
oh that one I thought you meant result
oh yeah it is
Result<T>
will either be "success, T" or "failure, here is my error"
and if its an unexpected exception, that means we couldnt forsee it, so we must wrap it in a general "ExceptionError"I would do:
in an update method for example
yeah kinda
this would be for an untyped error result type thou
where you dont have a value object for the error, only a string message
which is fine, its an active choice to make
here is a snippet from a web api that uses results
so I map my errors to http return codes like this
thats why I like having typed errors
I see, makes sense
we wanna share the message for every error of this kind
@Pobiega why does ArgumentException exist?! if thats not abusing exceptions for an expected issue and for control flow I don't know what is
@Pobiega and same concern for other exceptions. FileNotFoundException is not really an unexpected issue, thats the most common problem when reading a file and I can easily check it with File.Exists()
Well, the result pattern isnt really used in the BCL itself
and remember, most of these are from C# 1.0 which is over 20 years old at this point
BCL?
the "functional" wave that has hit C# in the past 10 or so years wasnt a thing back then
ah base core lib?
base class library
ye
so they just exist cuz they are relicts from old times? I should not be using them?
When I first started coding C# I was like "wow exceptions make error handling so easy!"
No, you should, in a way
yeah same. I learned programming in java and they commonly use exceptions for control flow
IO operations throw FileNotFound, you cant change that, so you either check that the file exists before reading it (I prefer this) or you try/catch it
argumentexception is actually a good exception imho
remember, exceptions are for the programmer
so if argument exception is ever thrown, it means a programmer fucked up
"oh I passed -1 and this thing only ever supports positive values, my bad"
but if we strictly follow "exceptions are for unexpected issues" than this violates it. invalid arguments is the most expected thing, we could just sanitize it if we wanted to. Or return our result type indicating whats wrong
I don't agree
a USER passing invalid data, thats expected
ohhhh
fair
a developer using a method wrong by passing the wrong arguments, thats not expected
fairrr
thx
so yeah, always validate user input
a related thing is
ArgumentNullException
there is a warning when you have nullable reference types on in public methods that take MyClass myClass
style arguments
"You flagged this as not nullable, but someone might still try to pass null, so you should verify that its not null"
seems silly, but because nullable annotations are just... annotations, you can still be given a null value. Since you've said "this class/method doesnt work if you pass null", you should verify that it isnt nullsure, that makes sense
thats why you often see
ArgumentNullException.ThrowIfNull(parameterName);
in constructors even if the parameter is not specified as accepting nullWas this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.