How to Solve an Error with Installing tidyverse Using R3.5 in Linux?

When I first tried to install tidyverse in R 3.5 on my Linux system, I ran into a frustrating wall of error messages. At first glance, they were overwhelming, but once I dug deeper, I realized they were pointing to a common issue: package version mismatches across R versions.

The First Code I Ran

Like many R users, I started with the straightforward approach:

install.packages("tidyverse")

But R fired back with:

namespace ‘vctrs’ 0.3.6 is being loaded, but >= 0.3.7 is required
ERROR: lazy loading failed for package ‘tidyr’
Error : .onLoad failed in loadNamespace() for 'rlang'
error: 'is_character' is not an exported object from 'namespace:rlang'

At this point, I felt stuck.

What the Error Really

  • tidyr needs vctrs ≥ 0.3.7. My R session was still holding onto an older vctrs (0.3.6).
  • The 'is_character'...from 'rlang' error was a namespace mismatch. I had a mix of old and new versions of rlang, vctrs, and friends (tibble, pillar, etc.). They depend tightly on each other, so mismatches cause load failures.
  • The root cause? I was sharing one user library across multiple R versions (R 3.5 and R 4.x). That mix is toxic for old R installs.

My Quick Checklist to Fix This (Linux, R 3.5)

  1. Restart R 3.5
    In R: q() (choose n when asked to save). Then relaunch with R35 in the terminal.
  2. Give R 3.5 its own clean library mkdir -p "$HOME/R/3.5-library" export R_LIBS_USER="$HOME/R/3.5-library" Adding the export line to ~/.bashrc made it permanent.
  3. Pin CRAN to an older snapshot
    Modern CRAN versions often drop R 3.5 support, so I pinned to mid-2021, when tidyverse still supported 3.5: options(repos = c(CRAN = "https://packagemanager.posit.co/cran/2021-06-01"))
  4. Install exact package versions that work together
    Here’s where I built a little helper function.

The One Shot Script That Solve It for Me

I ran this inside R 3.5:

## 0) Safety: show where we're installing
print(.libPaths())

## 1) Pin repo
options(repos = c(CRAN = "https://packagemanager.posit.co/cran/2021-06-01"))

## 2) Helper: install pinned version if missing
ensure_version <- function(pkg, version) {
  if (!requireNamespace("remotes", quietly = TRUE))
    install.packages("remotes")
  if (!requireNamespace(pkg, quietly = TRUE) ||
      utils::packageVersion(pkg) != version) {
    remotes::install_version(pkg, version = as.character(version),
                             upgrade = "never", dependencies = TRUE)
  }
}

## 3) Core low-level deps
ensure_version("rlang",  "0.4.10")
ensure_version("vctrs",  "0.3.8")
ensure_version("ellipsis","0.3.1")
ensure_version("glue",   "1.4.2")

## 4) Building blocks
ensure_version("tibble", "3.1.2")
ensure_version("pillar", "1.6.1")
ensure_version("hms",    "1.1.0")
ensure_version("cli",    "2.5.0")

## 5) Core tidyverse members
ensure_version("dplyr",  "1.0.7")
ensure_version("tidyr",  "1.1.3")
ensure_version("readr",  "1.4.0")
ensure_version("stringr","1.4.0")
ensure_version("purrr",  "0.3.4")
ensure_version("forcats","0.5.1")
ensure_version("ggplot2","3.3.5")

## 6) Finally, tidyverse meta-package
ensure_version("tidyverse", "1.3.1")

## 7) Smoke test
cat("\nLoaded versions:\n")
pkgs <- c("rlang","vctrs","dplyr","tidyr","tibble","pillar","hms","tidyverse")
print(sapply(pkgs, function(p) as.character(utils::packageVersion(p))))

This gave me a clean, working tidyverse stack under R 3.5.

My “Practice Utility” to Double Check Compatibility

I also wrote a quick checker:

check_tidyverse_compat <- function() {
  need <- list(
    rlang   = "0.4.10",
    vctrs   = "0.3.7",
    dplyr   = "1.0.0",
    tidyr   = "1.1.0",
    tibble  = "3.0.0",
    pillar  = "1.5.0",
    hms     = "1.0.0",
    tidyverse = "1.3.0"
  )
  have <- lapply(names(need), function(p) {
    if (requireNamespace(p, quietly = TRUE))
      as.character(utils::packageVersion(p))
    else NA_character_
  })
  df <- data.frame(
    package = names(need),
    have = unlist(have),
    need = unlist(need),
    ok = mapply(function(h, n) {
      if (is.na(h)) FALSE else utils::compareVersion(h, n) >= 0
    }, unlist(have), unlist(need)),
    stringsAsFactors = FALSE
  )
  print(df, row.names = FALSE)
  if (!all(df$ok)) {
    message("\nSome packages are too old or missing. Re-run the setup script.")
  } else {
    message("\nLooks good for R 3.5.")
  }
}

check_tidyverse_compat()

This gave me instant peace of mind after installation.

Common Pitfalls I Learn

  • Mixing libraries across R 3.5 and R 4.x caused almost all my namespace errors. Keeping .libPaths() separate solved it.
  • Updating packages without pinning CRAN pulled in versions that flat-out didn’t support R 3.5.
  • Running R35 --vanilla helped rule out side-effects from .Rprofile or saved workspaces.

Final Thought

At first, the error messages felt like a brick wall. But once I broke them down, I realized the solution was straightforward: isolate R 3.5’s library, pin package versions, and install in the right order. Now, not only do I have a working tidyverse on R 3.5, but I also have a reproducible script I can share with teammates. And the little compatibility checker I wrote gives me confidence that everything is consistent before I start scripting.

Related blog posts