Other sites

Error Handling in R

It’s often the case that I want to write an R script that loops over multiple datasets, or different subsets of a large dataset, running the same procedure over them: generating plots, or fitting a model, perhaps. I set the script running and turn to another task, only to come back later and find the loop has crashed partway through, on an unanticipated error. Here’s a toy example:

This skips over the error-causing non-numeric input with an error message (you can suppress the error message with the silent=T argument to try), and continues on with the rest of the input. Generally, this is what you would like.

The tryCatch block

Sometimes, however, you might want substitute your own return value when errors (or warnings) are returned. We can do this with tryCatch, which allows you to write your own error and warning handlers. Let’s set our loop to return log(-x) when x is negative (negative arguments throw a warning) and return a NaN for non-numeric arguments (which throw an error). We’ll print out an advisory message, too.

Whoops — not quite! We are correctly catching and messaging warnings and errors, but we are not printing out our desired corrected value. This is because the warning and error handlers are altering the execution order and throwing out of the print statement. If we want to return and print out the appropriate value when warnings and errors are thrown, we have to wrap our tryCatch into a function. We’ll leave the advisory message in.

Now we return and print out a valid numeric value for numeric inputs to robustLog, and a NaN only for non-numeric input. Notice also that log(0) still returns -Inf, with no warning or error.

Of course, now that we are writing a new function, it would make more sense to check the arguments before calling log, to avoid the recalculation. This example is only to demonstrate tryCatch, which is useful for defending against unexpected errors.

Advanced Exception Handling

The above is about as much about exception and error handling in R as you will usually need to know, but there are a few more nuances. The documentation for tryCatch claims that it works like Java or C++ exceptions: this would mean that when the interpreter generates an exceptional condition and throws, execution then returns to the level of the catch block and all state below the try block is forgotten. In practice, tryCatch is a bit more powerful than that, because you have the ability to insert custom warning and exception handlers. There is another exception handling routine called withCallingHandlers that similarly allows you to insert custom warning and exception handlers. There may be some difference in semantics or in environment context between tryCatch and withCallingHandlers; but we couldn’t find it.

The final concept in R’s error handling is withRestarts, which is not really an error handling mechanism but rather a general control flow structure. The withRestarts structure can return to a saved execution state, rather like a co-routine or long-jump. It can be used with withCallingHandlers or with tryCatch to design either interactive or automated “retry on failure” mechanisms, where the retry logic is outside of the failing function. Although obviously a function that checks for potential errors and alters its behavior before signaling a failure is much easier to maintain.

Here’s as simple an example of using restarts as we could come up with. The idea is that there is some big expensive computation that you want to do with the function input before you get to the potentially error-causing code. You want the exception handlers to mitigate the failure and continue running the code without having to redo the expensive calculation. Imagine this function as being part of a library of routines that you wish to call regularly.

By default, our example routine will enter R’s debugging environment upon exception. The user then has to select the appropriate restart function to continue the operation.