3

Problem

I wonder what best practices are to indicate implementation errors detected at runtime.

For example, if you write the following method (in Java):

public void doSth(int i) {
    try {
        runOp(i); // an operation possibly throwing FrameworkException
    } catch (FrameworkException e) {
        throw new RuntimeException("Implementation error: FrameworkException should never be thrown at this place.", e);
    }
}

and you know that by design exception FrameworkException will never be thrown but as it is a checked exception you have to handle it. Further you want to handle it because it helps debugging if it turns out that the exception can actually be thrown in cases you haven't thought of in the design. Then you want to communicate that this exception is due to an implementation error.

Possible solution

As in the example, throw an unchecked exception (as we don't want to recover) and say it is an "Implementation error" (as we got to a state which is illegal by design).

In Java there doesn't seem to be a standard exception for this, is there in other languages?

The best solution I see is to create a class ImplementationError extends RuntimeException and throw it in situations where you have detected an illegal state which must be caused by an implementation error. This is somewhat similar to assertion statements in Java which would terminate the program if a boolean condition is not met. However, assertions cannot be used to state that an Exception should not be thrown (as far as I know) and in addition assertions have to be enabled when running the program which they are not by default.

Going a step further towards language design, it would be nice if statements or blocks could be annotated with something like @IllegalThrow FrameworkException with which the exception doesn't have to and cannot be catched anymore with a try-catch block. Then ImplementationError would automatically be thrown when the exception occurs anyway.

The above example would then look as follows

public void doSth(int i) {
    @IllegalThrow FrameworkException
    runOp(i); // an operation possibly throwing FrameworkException
}

The compiler could simply generate the try-catch block in the first example to implement this feature. Maybe in Java this is even possible with annotation processing?

Questions

  • What is best practice to handle these situations? Is the suggested solution appropriate?
  • Are there any arguments against the suggested annotation?

Related questions

  • How should I handle exception that should never be thrown? is similar but the question is rather about whether or not to check for such illegal states. My point here is more that I do want to throw an appropriate exception (I think it is best practice to give reasons as precise as possible for a crash) and rather ask what kind of Exception should be thrown (class, parameters, ...). Further I want to look at language design, i.e. whether there are better ways to express that you don't expect an exception to be thrown.
  • How to deal with checked exceptions that cannot ever be thrown (linked as duplicate of the previous question) again is a bit different because it asks about exceptions that actually cannot be thrown as opposed to exception where you declare their occurrence as illegal by design.

Both questions have interesting and helpful answers but I think this question might have different answers.

  • Possible duplicate of How should I handle exception that *should* never be thrown? – gnat Dec 8 '17 at 13:38
  • @gnat Thank you for the hint, I have updated my question commenting on related questions. – user905686 Dec 8 '17 at 13:58
  • This is exactly what I do. Usually it takes the form of a custom class ProgrammingError extends RuntimeException. – Kilian Foth Dec 8 '17 at 14:02
  • Don't know what's idiomatic in Java but in C and C++, assert is used for that purpose to distinguish programmer errors (accessing an array out of bounds) from external input errors (trying to read from a corrupt file, which is not a fault of the programmer). assert(n >= 0 && n < len); /* this should never happen! */ In C and C++, it has the benefit that it doesn't cost anything in release builds so it doesn't slow down production performance, only debug builds. More importantly, it concisely separates the idea of a programmer error from a real exceptional case. – user204677 Dec 8 '17 at 14:26
2

What is best practice to handle these situations? Is the suggested solution appropriate?

Looked up the Java documentation and it's similar to C and C++. You have assert specifically for this purpose.

14.10. The assert Statement An assertion is an assert statement containing a boolean expression. An assertion is either enabled or disabled. If the assertion is enabled, execution of the assertion causes evaluation of the boolean expression and an error is reported if the expression evaluates to false. If the assertion is disabled, execution of the assertion has no effect whatsoever.

Like C and C++, you can disable the effect of assertions so that they don't slow down your release builds if you want.

assert d >= 0 && d <= s.length(); // this should never happen!

If the assertion fails (condition is false), then the language throws an AssertionError exception which clearly indicates that it's a programming error and not due to something beyond the programmer's control (ex, a corrupt external file).

It's often very important to distinguish a programmer error from external input error, because you don't want to write some robust program that starts hiding programmer errors by trying to gracefully recover from every possible exception. With programmer errors, it's often instead desirable not to gracefully recover and instead bring the software to a screeching halt so that it can never be ignored and demands an immediate fix.

How should I handle exception that should never be thrown?

I think you can just let them propagate to the point of displaying an error message in a console or just output and shutdown whenever you catch AssertError unless you're working in a mission critical software which wants to try to resume and continue operating even in the case of a critical programmer mistake. In C and C++ it instantly brings the software to a halt giving us the line number where the assertion failed and often a way to immediately start debugging it which is actually generally the most desirable thing to do in the case of something that should never happen as a result of a programmer mistake.

How to deal with checked exceptions that cannot ever be thrown [...]

Here I have some C++ bias and I'm not sure if my suggestions are idiomatic in Java, but since the question is somewhat language agnostic, I don't think you should be catching so frequently.

A try/catch should model the concept of a user-oriented transaction (the user could be a programmer using a library in some cases) to be completed in full or rolled back as a whole in the event of a failure. It's not akin to C-style error code propagation manually down the call stack. If you try to catch every possible error that could be thrown immediately after it's thrown, and also try to catch every possible exception as specifically as possible, that defeats a lot of the purpose of exception handling. The richness of exception handling comes from minimizing the number of functions in your system that have to be concerned with handling errors and minimizing the amount of knowledge they need about the error through inheritance and polymorphism. Throwing should go all the way down the call stack to a catch site where it makes the most sense to recover and report the error from the standpoint of the user operation being performed with automated cleanup along the way as it skips past functions and goes straight to the place where exception recovery makes the most user-oriented sense.

Exception handling becomes useful when you don't have to place so many catch blocks and can catch exceptions as generally as possible, not caring so much what type of exception it is provided that you can gracefully recover from it, rolling back any side effects along the way to put the system back into a clean state as though the operation never happened and extracting whatever basic information you need to report the error to the user as necessary. That doesn't require you to litter your code trying to catch things that could never throw at every site that could throw.

For example, there could be hundreds of function calls that could fail in the process of loading a file, but a file loader shouldn't have hundreds of catch blocks trying to catch every one of them individually, otherwise it becomes just as cumbersome or more than manual error code propagation. In many cases a file loader should be able to just get away with one try/catch block for the entire file loading operation/transaction. All that matters really is whether the file loaded or couldn't load after all that's done. If it failed it's only of slight interest to know whether it failed due to a memory allocation failure, a failure to load an external resource, a corruption in the file, etc, which you can extract often just from the message attached to the exception without trying to catch every single possible type that could ever be thrown (which is impossible to foresee if the functions the file loader calls might be changed in the future by people other than you). So try to catch more generally and less frequently, and don't try to catch every single possible (and impossible) exception for every single function that could throw. Just let those functions throw past your functions to a catch site where it makes the most user-oriented sense to recover from the error and report it.

So for example if you have a call stack like:

load_file(...)->f1(..)->f2(...)->f3(...)
    ^
only place that makes user sense
to recover from errors

Then f1, f2, and f3 above probably don't need to bother trying to catch and handle any kind of exception, since they're not recovery sites. load_file is the recovery site. So for whatever library functions f1 to f3 call, I wouldn't bother trying to catch them or catch them specifically. Catch generally and infrequently at places where it makes sense. Distinguish functions which don't need to recover from and report errors to the user from ones that don't. If you follow this and catch at a coarse enough level, not granular level where you're worried about every possible exception that could be thrown with every single function call, you shouldn't have to worry about what to do in scenarios where some function can throw an exception but never with the inputs you provide. Let the file loader deal with it which catches exception types as generally as possible.

1

There are a lot of classes of error that cannot be handled in any meaningful way. Bugs in code is one. Errors in configuration is another. In fact there are innumerable ways that things could go wrong in unlikely or unexpected ways. What to do about that? By default, the answer is to have the application crash. One of the big mistakes I see in programs is trying handle unhandleable situations. This leads to applications continuing in a improper state. In my experience, this is much worse than a crash as an application in such a state can create a lot of havoc (e.g. corrupting data, causing failures in other applications) but almost never do anything good.

  • So what to do about it? Fail-fast.
  • In Java how can we easily do that? Throw a RuntimeException.
  • Should I subclass it? Maybe? What does that buy you?

If you are going to try to have specific error handling for a certain class of error, then subclassing RuntimeException is a great solution. If you are just going to let the stacktrace go to sys.err, then why bother? just wrap in RuntimeException and move on with your life. If it ever happens, you will know that you had an unhandled error and the stacktrace will tell you exactly where it came from.

  • Throwing a dedicated ImplementationError inheriting from RuntimeException makes it clear that you have already thought of the cases when the causing exception might be thrown (and came to the conclusion that it should never be thrown and thus its occurrence must be a bug). A RuntimeException however can come from anywhere (also 3rd party code) and does not necessarily indicate a bug: the program can get into an unrecoverable faulty state not caused by the programmer, e.g. because of missing resources. It's not a big deal to define a separate class and it gives you a clearer distinction. – user905686 Dec 9 '17 at 10:10
  • @user905686 There's nothing wrong with creating your own exception classes. If it helps, you should absolutely do it. I just don't see a lot of value here if you aren't going to catch the custom exception. – JimmyJames Dec 11 '17 at 15:57
  • 1
    Wait. What? Throwing a RuntimeException is fail-fast in Java? I'd rather say it's more of a muddle-on, at least in the (admittedly rather weak, and catch riddled) code I have to maintain from time to time. – Martin Ba Dec 21 '17 at 12:53
  • @MartinBa Yes shutting down the application is exactly that, failing fast. Continuing to execute in an invalid state is "muddle-on". I'm adding a link to the Martin Fowler article if you are still unsure. – JimmyJames Dec 21 '17 at 15:59
  • 1
    @Jimmy yeah but how is throwing a RuntimeException going to shut down the app?? – Martin Ba Dec 21 '17 at 16:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.