Check when install.packages fails in R

I recently found out that when install.packages() fails, it only returns a warning and not an error, i.e., the exit code is 0.

R -q -e 'install.packages("thisdoesnotexist!!!")'
> install.packages("thisdoesnotexist!!!")
Installing package into ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3’
(as ‘lib’ is unspecified)
Warning message:
package ‘thisdoesnotexist!!!’ is not available for this version of R

A version of this package for your version of R might be available elsewhere,
see the ideas at
https://cran.r-project.org/doc/manuals/r-patched/R-admin.html#Installing-packages
echo $?
# 0

The exit code is still zero when an existing package fails to install; for example:

 R -q -e 'install.packages("ggstatsplot")'
snipped
ERROR: configuration failed for package ‘nloptr’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/nloptr’
ERROR: dependency ‘nloptr’ is not available for package ‘lme4’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/lme4’
ERROR: dependency ‘lme4’ is not available for package ‘pbkrtest’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/pbkrtest’
ERROR: dependency ‘lme4’ is not available for package ‘lmerTest’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/lmerTest’
ERROR: dependencies ‘pbkrtest’, ‘lme4’ are not available for package ‘car’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/car’
ERROR: dependencies ‘lme4’, ‘pbkrtest’, ‘lmerTest’, ‘car’ are not available for package ‘afex’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/afex’
ERROR: dependencies ‘afex’, ‘PMCMRplus’ are not available for package ‘statsExpressions’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/statsExpressions’
ERROR: dependency ‘statsExpressions’ is not available for package ‘ggstatsplot’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/ggstatsplot’

The downloaded source packages are in
        ‘/tmp/Rtmpy3R57N/downloaded_packages’
There were 11 warnings (use warnings() to see them)
echo $?
# 0

If you install packages interactively, in an R prompt or using RStudio, you would see that the install failed. But when building images for containers, we rely on exit codes >0.

For Singularity, one easy way is to try to load the library in the test section.

Another way, as suggested by a SO user that mimics the testing approach with Singularity, is to write a function that tries to install a package and then checks whether the package can be loaded.

#!/usr/bin/env Rscript

packages = commandArgs(trailingOnly=TRUE)

for (l in packages) {

    install.packages(l, dependencies=TRUE, repos='https://cran.rstudio.com/');

    if ( ! library(l, character.only=TRUE, logical.return=TRUE) ) {
        quit(status=1, save='no')
    }
}

The code above can be simplified as pointed out by Yihui:

install.packages(packages, dependencies=TRUE, repos=’https://cran.rstudio.com/’)
for (l in packages) library(l, character.only=TRUE)

I also came up with an approach using tryCatch, which turns all warnings into errors. To demonstrate, I'll show a failed install without tryCatch and the exit code.

R -q -e 'install.packages("beepr", repos = "https://cran.ism.ac.jp/")'
> install.packages("beepr", repos = "https://cran.ism.ac.jp/")
Installing package into ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3’
(as ‘lib’ is unspecified)
trying URL 'https://cran.ism.ac.jp/src/contrib/beepr_1.3.tar.gz'
Content type 'application/x-gzip' length 922244 bytes (900 KB)
==================================================
downloaded 900 KB

* installing *source* package ‘beepr’ ...
** package ‘beepr’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
Error in dyn.load(file, DLLpath = DLLpath, ...) : 
  unable to load shared object '/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/audio/libs/audio.so':
  /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /home/dtang/R/x86_64-pc-linux-gnu-library/4.3/audio/libs/audio.so)
Calls: <Anonymous> ... namespaceImport -> loadNamespace -> library.dynam -> dyn.load
Execution halted
ERROR: lazy loading failed for package ‘beepr’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/beepr’
* restoring previous ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/beepr’

The downloaded source packages are in
        ‘/home/dtang/tmp/RtmpLHq2eK/downloaded_packages’
Warning message:
In install.packages("beepr", repos = "https://cran.ism.ac.jp/") :
  installation of package ‘beepr’ had non-zero exit status
echo $?
# 0

Now with tryCatch.

R -q -e 'tryCatch(warning = function(x) stop(paste0("Warning detected: ", x$message)), install.packages("beepr", repos = "https://cran.ism.ac.jp"))'
> tryCatch(warning = function(x) stop(paste0("Warning detected: ", x$message)), install.packages("beepr", repos = "https://cran.ism.ac.jp"))
Installing package into ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3’
(as ‘lib’ is unspecified)
trying URL 'https://cran.ism.ac.jp/src/contrib/beepr_1.3.tar.gz'
Content type 'application/x-gzip' length 922244 bytes (900 KB)
==================================================
downloaded 900 KB

* installing *source* package ‘beepr’ ...
** package ‘beepr’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
Error in dyn.load(file, DLLpath = DLLpath, ...) : 
  unable to load shared object '/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/audio/libs/audio.so':
  /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /home/dtang/R/x86_64-pc-linux-gnu-library/4.3/audio/libs/audio.so)
Calls: <Anonymous> ... namespaceImport -> loadNamespace -> library.dynam -> dyn.load
Execution halted
ERROR: lazy loading failed for package ‘beepr’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/beepr’
* restoring previous ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/beepr’
Error in value[[3L]](cond) : 
  Warning detected: installation of package ‘beepr’ had non-zero exit status
Calls: tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
Execution halted
echo $?
# 1

The tryCatch approach has the advantage that the installation stops immediately after a warning is caught. This is handy for packages like {ggstatsplot}, which has a lot of dependencies; this is because the installation continues trying to install all dependencies despite one of the dependencies failing to install.

R -q -e 'tryCatch(warning = function(x) stop(paste0("Warning detected: ", x$message)), install.packages("ggstatsplot", repos = "https://cran.ism.ac.jp"))'
snipped
configure: error: Header file gmp.h not found; maybe use --with-gmp-include=INCLUDE_PATH
ERROR: configuration failed for package ‘gmp’
* removing ‘/home/dtang/R/x86_64-pc-linux-gnu-library/4.3/gmp’
Error in value[[3L]](cond) : 
  Warning detected: installation of package ‘gmp’ had non-zero exit status
Calls: tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
Execution halted

The installation immediately stops after the {gmp} dependency fails to install.

The downside of the tryCatch approach is that sometimes warnings are harmless. In those cases, the try-to-install and then try-to-load approach is safer.

Print Friendly, PDF & Email



Creative Commons License
This work is licensed under a Creative Commons
Attribution 4.0 International License
.
2 comments Add yours
  1. if ( ! library(l, character.only=TRUE, logical.return=TRUE) ) {
    quit(status=1, save=’no’)
    }

    That’s equivalent to

    if ( ! require(l, character.only=TRUE) ) {
    quit(status=1, save=’no’)
    }

    But I think you can simply do library(l, character.only = TRUE), which will throw an error and halt R if the package is not loadable. So the code

    for (l in packages) {

    install.packages(l, dependencies=TRUE, repos=’https://cran.rstudio.com/’);

    if ( ! library(l, character.only=TRUE, logical.return=TRUE) ) {
    quit(status=1, save=’no’)
    }
    }

    can be simplified to two lines:

    install.packages(packages, dependencies=TRUE, repos=’https://cran.rstudio.com/’)
    for (l in packages) library(l, character.only=TRUE)

    BTW, thank you very much for your sponsorship!

    1. Thanks for the simplified code snippet! And thank you for all the amazing packages! I was happy to hear that you found a new opportunity!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.