When programming an API, or any kind of application, software, ecc , at some point in the code you know that something could go wrong and you should notify it as an error, now, should I throw it? Or should I just log it as an error and let the program continue execution anyway? Or should I do both?
Let me know what you think are the best practices to notify errors and how you usually manage this situation.
Btw this is my first HN post:)
Thanks!
Are you writing a library? Make it convenient for the user to handle errors - in a way that is consistent with the language. E.g. in Go, return an error. In Python, raise an exception.
Are you writing a CLI program? Print to stderr, and abort program if execution cannot be safely continued.
Are you writing a GUI program? Create an error dialog box, and abort program if execution cannot be safely continued.
Are you writing a web service? Return the appropriate error code (or the equivalent in whatever protocol you use) and a human-readable error message about what happened.
Et cetera.
For something like a library: no logging, that's not a library's job -- unless it's a logging library! If the language has exceptions, then throwing only subclasses or implementations of a single exception parent/interface from the entire library is nice for clients of the library.
> “In applications where the accepted practice is to log an Exception and then rethrow it, you end up with miles-long logs that contain multiple instances of the same exception. In multi-threaded applications debugging this type of log can be particularly hellish because messages from other threads will be interwoven with the repetitions of the logged-and-thrown Exception. Instead, exceptions should be either logged or rethrown, not both.”
Then when the customer calls you and says "it crashes but i have no idea what i was doing" you can just have them send you the log instead of trying to guess what happens.
I invoke a programmable breakpoint, so that if I happen to be debugging that code, I get the debugger setup where I want it!
Note this is only appropriate for things that should "never" happen.
From the article:
People have asked why I don’t like programming with
exceptions. In both Java and C++, my policy is:
1. Never throw an exception of my own
2. Always catch any possible exception that might be thrown by a library I’m using on the same line as it is thrown and deal with it immediately.
I've been coding this way for ~10 years now and it's made things a lot easier for me. If the user should know about the error, let them know about it, if it should be hidden from the user, I'll track it in something like Sentry or Datadog and send out an alert for whoever's on call.
Otherwise, I would always throw and then only log errors in a single place within your application code. This avoids the need to be able to inject the correct type of logger into library code, etc. And makes it easier to attach other information about the execution of the application into the eventual error log line.
The important thing is that your errors are clearly understandable at the point that they are logged, therefore I also recommend creating your own granular application-level error types that wrap any underlying errors, writing sensible error messages, and attaching contextual state about the process that failed.
Of course it is mandatory to never ever put anything useful in the Windows Event Viewer. Ever. Absolutely forbidden. Only useless messages are ever allowed in there. So many times I've had an application crap out and I check the event viewer thinking this time will be different. It never is.
Normally I leverage the underlying logging and get my code to write to its own log file. I like it if I can independently turn on the logging and the verbosity for my module, even if logging is turned off for performance reasons in the main code.
Once everything actually works you might end up with 'carpet logging' in your code, where every bit of data is getting logged just in case there is an error. You might also have catch blocks around everything.
At this stage you can remove the excess logging and allow exceptions to bubble up, resisting the temptation to refactor anything else. If this works and you have something stable, then you can optimise your code without it being 'premature'.
I agree with @jameshush, however, if the code you are working with throws exceptions and has a sophisticated way of dealing with them, then you can throw exceptions that are going to work with what there is already.
If it’s something impossible to handle - say a db failure after n retries- then return a 500 with some info but not anything sensitive. No db names, queries, etc.
Sometimes your exceptions tell you about additional validation you need to add. Ie users keep passing in invalid ids and I get a db exception, maybe I should instead check for the row first and return a 404.
Log all the things.
There are standards for REST error responses: https://blog.restcase.com/rest-api-error-handling-problem-de....
* Throw exceptions when things are broken
* Catch exceptions when the calling code can do something other than propagate the error
* Log exceptions at the level where they are uncaught
* Log information about errors that are handled rather than the exception detail
* Never log and propagate the error. This often leads to double logging, especially when the error
is further propogated. This in turn causes confusion about whether there is one error or two, as
well as whether one error is a cause or is caused by the other error.
* Always provide enough information in your own exceptions about:
* what you were trying
* what went wrong
* what the caller can do to fix the issue (even if that's an indication that nothing can be done)
* Never discard information about an exception unless it will truly never serve any purpose
* Consider storing (but not writing) debug log messages that are only used when something does crash.
How we got to this point is usually the most important thing a developer needs to know. Google "Ring
Buffer Logging"
* Always think about how you would prevent the error from happening rather than letting it fail
* Log errors at system boundaries and report useful errors to the external system
* Never include confidential info in errors (passwords, keys, tokens)
* Ensure errors can be correlated in some way (request ID / correlation ID etc).
* Ensure that the contextual level of the exception matches the level that the caller expects.
E.g. an error throw by code that saves a customer record generally shouldn't ever be a FileNotFound
or a DivideByZero error even if this is what happened somewhere down the stack.
These are generalizations, and like many rules can be broken if it makes sense. Do so with good reason though.
* If it's a useful error for another developer (or yourself) to see, log it.
* Use different loglevels in your code so another developer (or yourself) can increase or decrease the verbosity of your logging in different environments and under different circumstances.
Whether you just throw an exception or handle it yourself and package any error into a neat error object has advantages and disadvantages. Disadvantage to just throwing an exception is that the user might not get a sensible context. The call stack might not mean much to the user if he doesn't know about internal workings. But the advantage is that there is a uniform way of handling exception. The user immediately knows that something went wrong if an exception is thrown somewhere on a lower level. If the API handles the exception itself, it might be able to add the relevant information as to what caused the error, especially for common errors. In that case the user needs to know how to parse the error responses of your API though.
You should never just let the program run after it encountered an exception without informing the user. A log cannot sensibly inform an application about errors at runtime and this "swallowing" of exceptions is much more difficult for the user to handle or even to know if something went wrong.
I think the only rule of thumb is really that it is primarily the responsibility of the user to handle error and log application messages. But when I think of API I think of interfaces to specific devices or software, there are completely different rules for web APIs. Those cannot just throw exception to the user anyway. They also often have many users within one execution environment, so internal logging can certainly make sense.
Another topic is that error != exception and different rules might apply.
This serves two purposes. One, if for whatever reason you end up with a scenario you hadn't anticipated, it will be picked up and the program will exit gracefully, informing you exactly which supposedly impossible condition was violated.
The second purpose is that, often enough, inserting such an "impossible error" branch makes the preceding code much clearer.
I'm so used to this idiom, in fact, that I now rarely have an 'else' clause catching a proper fallback case, when dealing with an if/else block. I treat the fallback case via another elseif, and almost always the else simply raises an error if triggerred.
There is no single solution.
For example your database is down - only thing that you can do is to show user error "please try again" - while you should have detailed logs in place to find why application cannot connect to database. Your program most likely cannot continue even on other screens so you most likely throw exception that should bubble up to reach interface and stop any execution.
Other scenario some third party api call failed - your user does not care and he wants to continue working, you probably want to show warning "you can continue but our systems cannot reach XYZ". So you don't want to throw an exception because you still want let user work on whatever they need. You still want to have it logged with all details to check what is the problem.
If it's safe for the user to proceed with a default or empty value, then you can probably log the error and proceed. For example if you have a page which shows a post, and for some reason the author's name is null, you could probably show the page and not display the element containing the author's name, that way the user can at least read the post.
If it's not safe to continue - say the post body itself is null for some reason, you can throw an exception as well and show the user an error.
In both cases, especially the latter, you'll want to have some kind of monitoring enabled that notifies you of the problem along with the relevant logging of context that will help you diagnose the source of the problem.
Then I free resources and let the exception reach user action level, where an informative, actionable error is shown to the user.
The caller can then recover from the exception by searching else where or using a default etc.
If the exception can not be recovered then its an error. If its a user error like asking for a result that doesn't exist ill notify the user without logging. If its an unexpected error the exception is unhandled and bubbled up to a global exception handler that logs to a file.
Some times ill setup an email on error if the code is important enough.
I personally find that exceptions are very good to handle rare runtime failures that are not expected, and that severely hamper the flow of a program. In general, these cases tend to be rare, and under their circumstances terminating is generally ok. Still, it's nice to give the developer some control over if and when they want to handle it, especially if it is a library.
Return an error with a proper HTTP error code to the client, log what happened and exit the process.
Every attempt at trying to recover from errors will eventually fail, in most cases it's better just to fail.
It's more about the categorization and impact than the implementation. Feel free to send me an e-mail if you have questions.
There is an alternative to returning error codes or throwing exceptions: Put the errors on some context handle - e.g. set an error flag. This taints the context where the error occurred, and if you do it right you can contain the error and replace / restart / shutdown that context in isolation. And importantly, it allows the client to do the error handling at a more central point.
This kind of design can require that you do a little "paranoid" error checking at several entry points in the implementation of your API, but it's well worth it given that you save a lot of immediate error checking on the client side. Also, if you structure things right you can minimize the need for error checking in the implementation itself.
A simple and well-known example of this approach in C would be the FILE API from stdio.h (fopen()/fread()/ferror()).
This and returning NULL/error codes is how I structure my applications. I have a strong distaste for exceptions, for the same reasons mentioned in the other post citing Joel Spolsky. But exceptions are fine for quick and dirty scripts. And some people maintain that you should structure whole applications purely with RAII and rely on exceptions to cleanup your contexts perfectly and immediately. I'm not convinced that this is practical, but honestly as probably most, I'm content just living in my own bubble...
* If you are doing a library you can safely let the Error do the thing.
* If you are building a web/API catch all the errors, log them and send a nice generic error to the client.
* If you are doing a CLI tool I prefer to do like the library but if it's for a final client probably I'd try to do it like with the web/API.
How can you even get to such a conclusion? These kind of attitude is what makes programs buggy.
Plant an error tracker, so thrown errors are automatically logged, so you can debug later but don't make the situation worse by continuing on.
My contribution is a slight tangent, specific to React and Remix.run: take a look at the docs and official blog posts about ErrorBoundary and in Remix also CatchBoundary for good examples / patterns.
Throwing the error is useful when callers of a function need to know that error has occurred, otherwise it should be handled within the function.
1. How “let it fail” leads to simpler code. https://news.ycombinator.com/item?id=32080903
As for your question: In what language? What ecosystem?
Different languages have different error handling paradigms, and "customs" (aka idiomatic code). Some languages have even changed slightly over the years. It also depends on other, existing methods in the API, if it's an existing one.
Try to be consistent and not to surprise your users.
It also depends on what you mean by an "API". The original meaning of this phrase (calling inside the boundary of a single executable, or between applications using a more or less invisible RPC mechanism) has largely changed in recent years to mean Web APIs. Most answers around me allude to this.
My own take is below. My experience is mainly with C, C++ and C#, with some dabbling with Haskell, D, Lisp, Rust, and JavaScript.
1) Never throw exceptions, except under exceptional circumstances, when your program should crash anyway since you're in a point in the code in which you have no idea how to continue. I like Rust's approach that where there aren't any catch-able exceptions, only panics that always crash the application.
2) In particular, don't throw exceptions for bad user input, bad data from a file or database, error codes from another website or API. You know and you should handle them inside your API. Don't let exceptions from lower-levels APIs you use propagate upward. They don't mean anything to the caller and effectively expose your internal implementation.
3) It might be ok to throw exceptions inside your method if it makes your life a bit easier, if you catch them at some topmost level and return an error. It depends on the language you use.
4) A crashing application should create a dump file, which make analyzing errors in production much easier and quicker. This doesn't concern on you as the writer of a lower-level API, but it something you should raise with your direct or indirect consumers.
5) A slightly better way is to use return codes. But it's still not very good. In C for example that's about the only thing you have, except a global error code and that's even worse.
6) A better way is to use Option/Result types, but not all languages will have them and if they do they might not be idiomatic, and some of your users will suffer since you force upon them an unfamiliar paradigm and/or external dependencies. Also even Option/Result type often make the calling code devolve into if/else chains, unless the language has support for monadic chaining, and while you can find it, it's not idiomatic in many languages (yet?). Also few languages will force your callers to handle those returned errors.
7) That said, exceptions are your friends during development and also after - throw them when you reach a point in the code that you know you should never reach. This is if you can't make the error situation somehow fail compilation.
8) As for logging - yes you should log each error. My take on logging - if it's a lower level API code, it should never do logging by itself, unless it will always be a part of larger framework that already has an integrated logging. What I usually do is accept an optional logging callback to let the caller implement the logging.
On Error Resume Next
Who needs errors anyway?