rerror-handlingpackage

force R to always display package name in error message


I sometimes load multiple packages that export functions of the same name. For example, both Hmisc and dplyr have a summarize function, and I sometimes load both packages. I then call summarize, thinking that I am calling dplyr::summarize when I am really calling Hmisc::summarize. When that happens, I get an error message like this:

Error in summarize(., mean = mean(instRating)) : 
  argument "by" is missing, with no default

The message is initially hard to understand, because my code contains no errors. It takes me a minute to realize that I've called a function in the wrong package. The error message would be easier to understand if its first line included the name of the relevant package:

Error in Hmisc::summarize(., mean = mean(instRating)) : 

Is there a way to force R to display the package name in these error messages?

I know that I can get around the problem by typing out dplyr::summarize or by changing the order in which I load packages. But my interest lies with adding detail to R's error messages.


Solution

  • From the documentation of the base option error:

    'error': either a function or an expression governing the handling of non-catastrophic errors such as those generated by 'stop' as well as by signals and internally detected errors. If the option is a function, a call to that function, with no arguments, is generated as the expression. By default the option is not set: see 'stop' for the behaviour in that case. The functions 'dump.frames' and 'recover' provide alternatives that allow post-mortem debugging. Note that these need to specified as e.g. 'options(error = utils::recover)' in startup files such as '.Rprofile'.

    So it should be possible to define a function that returns the name of the package where the function that’s throwing the error lives. For example:

    library(dplyr)
    library(Hmisc)
    data(mtcars)
    
    print_package <- function() {
        calls <- sys.calls()
        call <- calls[[length(calls) - 1]]
        fun.name <- as.character(call)[1]
        pkg.name <- sub("package:", "", getAnywhere(fun.name)$where[1], fixed = TRUE)
        message (paste0("In ", pkg.name))
    }
    
    options(error = print_package)
    
    summarize(mtcars$mpg)
    

    Returns:

    Error in summarize(mtcars$mpg) : 
      argument "by" is missing, with no default
    In Hmisc
    

    Edit (using rlang::trace_back)

    It turns out that there is a much cleaner way of doing this (credit goes to Hadley Wickham and his "Advanced R, Second edition"):

    library(dplyr)
    library(Hmisc)
    data(mtcars)
    
    print_trace_back <- function() {
        print(rlang::trace_back(bottom = sys.frame(-1)))
    }
    
    options(error = print_trace_back)
    

    Seems to handle errors gracefully:

    > summarize(mtcars$mpg)
    Error in summarize(mtcars$mpg) : 
      argument "by" is missing, with no default
        █
     1. └─Hmisc::summarize(mtcars$mpg)
    >
    > Hmisc::summarize(mtcars$mpg)
    Error in Hmisc::summarize(mtcars$mpg) : 
      argument "by" is missing, with no default
        █
     1. └─Hmisc::summarize(mtcars$mpg)
    >
    > summarize(mtcars$mpg, as.character(mtcars$apa), mean)
    Error in tapply(X, INDEX, FUN, ..., simplify = simplify) : 
      arguments must have same length
        █
     1. └─Hmisc::summarize(mtcars$mpg, as.character(mtcars$apa), mean)
     2.   └─Hmisc::mApply(X, byc, FUN, ..., keepmatrix = nc > 1)
     3.     └─base::tapply(X, INDEX, FUN, ..., simplify = simplify)
     4.       └─base::stop("arguments must have same length")
    >
    > (function() stop("Error"))()
    Error in (function() stop("Error"))() : Error
        █
     1. └─(function() stop("Error"))()
     2.   └─base::stop("Error")