Summary
When repos passed to plan_rev_dep_checks() / checker$new() includes an
R-universe repository, the check run fails during package source resolution with:
Error in db[package, ] : subscript out of bounds
Calls: <Anonymous> ... check_path -> check_path.pkg_origin_repo -> get_package_source
The root cause is that strip_src_contrib() compares a package's Repository
field to contrib.url(repo) with ==, but R-universe reports Repository as a
full, per-package tarball URL rather than a plain src/contrib path. The
equality check never matches, strip_src_contrib() returns character(0), and
get_package_source() then indexes an empty available.packages() database.
Tested with checked 0.5.4.
Reproducible example
repos <- c(
"https://ddsjoberg.r-universe.dev", # any R-universe repo
"https://cloud.r-project.org"
)
ap <- available.packages(repos = repos)
# R-universe reports the Repository field as a full tarball URL, not a
# contrib.url:
ap["gtsummary", "Repository"]
#> "https://ddsjoberg.r-universe.dev/src/contrib/gtsummary_2.5.1.9015.tar.gz?sha256=962c...&file="
contrib.url("https://ddsjoberg.r-universe.dev")
#> "https://ddsjoberg.r-universe.dev/src/contrib"
# checked:::strip_src_contrib() matches Repository against contrib.url() with `==`:
checked:::strip_src_contrib(ap["gtsummary", "Repository"], repos = repos)
#> character(0) # <- no match, returns empty
Because the stripped repo is character(0), pkg_origin_repo() stores an empty
repos, and at check time get_package_source() does:
db <- available_packages(repos = character(0)) # does not contain the package
pkg <- db[package, ] # subscript out of bounds
In a full run this surfaces as (checking cards' reverse dependencies against
the ddsjoberg R-universe):
checked::check_rev_deps(
path = ".",
repos = c("https://ddsjoberg.r-universe.dev", "https://cloud.r-project.org")
)
#> Error in db[package, ] : subscript out of bounds
Root cause
For a CRAN-style repo, Repository is the contrib path, so
contrib.url(repo) == Repository holds:
Repository: https://cloud.r-project.org/src/contrib
contrib.url(): https://cloud.r-project.org/src/contrib # equal
For R-universe, Repository is the concrete tarball URL for that package/version:
Repository: https://<user>.r-universe.dev/src/contrib/<pkg>_<ver>.tar.gz?sha256=...&file=
contrib.url(): https://<user>.r-universe.dev/src/contrib # not equal
strip_src_contrib() (R/utils-pkg-source.R) therefore returns nothing:
strip_src_contrib <- function(x, repos) {
match <- vlapply(repos, function(r) {
utils::contrib.url(r) == x # exact-equality fails for R-universe
})
repos[match]
}
which propagates through pkg_origin_repo() (R/pkg_origin.R) into
get_package_source() (R/utils-pkg-source.R):
get_package_source <- function(package, repos, db = NULL, destdir = NULL) {
if (is.null(db)) {
db <- available_packages(repos = repos) # repos == character(0)
}
pkg <- db[package, ] # subscript out of bounds
...
}
Suggested fix
Match by prefix rather than equality, so a per-package tarball URL still maps
back to its originating repo:
strip_src_contrib <- function(x, repos) {
match <- vlapply(repos, function(r) {
startsWith(x, utils::contrib.url(r, type = "source"))
})
repos[match]
}
It may also be worth having get_package_source() build the archive URL
directly from the Repository field when it is already a full tarball URL
(as R-universe provides), or resolve the source via utils::download.packages(),
which handles R-universe Repository URLs natively.
Workaround
For anyone hitting this before a fix lands, the two internals can be patched in
place at the top of a check script:
loadNamespace("checked")
assignInNamespace("strip_src_contrib", function(x, repos) {
repos[vapply(repos, function(r) startsWith(x, utils::contrib.url(r, type = "source")), logical(1))]
}, ns = "checked")
assignInNamespace("get_package_source", function(package, repos, db = NULL, destdir = NULL) {
if (is.null(db)) db <- utils::available.packages(repos = repos)
if (is.null(destdir)) {
pkg <- db[package, ]
return(sprintf("%s/%s_%s.tar.gz", pkg["Repository"], pkg["Package"], pkg["Version"]))
}
res <- utils::download.packages(package, destdir = destdir, available = db, repos = repos, type = "source")
unname(res[1L, 2L])
}, ns = "checked")
Session info
checked 0.5.4
- Reproducible with any R-universe repo in
repos (e.g. https://<user>.r-universe.dev)
Summary
When
repospassed toplan_rev_dep_checks()/checker$new()includes anR-universe repository, the check run fails during package source resolution with:
The root cause is that
strip_src_contrib()compares a package'sRepositoryfield to
contrib.url(repo)with==, but R-universe reportsRepositoryas afull, per-package tarball URL rather than a plain
src/contribpath. Theequality check never matches,
strip_src_contrib()returnscharacter(0), andget_package_source()then indexes an emptyavailable.packages()database.Tested with
checked0.5.4.Reproducible example
Because the stripped repo is
character(0),pkg_origin_repo()stores an emptyrepos, and at check timeget_package_source()does:In a full run this surfaces as (checking
cards' reverse dependencies againstthe
ddsjobergR-universe):Root cause
For a CRAN-style repo,
Repositoryis the contrib path, socontrib.url(repo) == Repositoryholds:For R-universe,
Repositoryis the concrete tarball URL for that package/version:strip_src_contrib()(R/utils-pkg-source.R) therefore returns nothing:which propagates through
pkg_origin_repo()(R/pkg_origin.R) intoget_package_source()(R/utils-pkg-source.R):Suggested fix
Match by prefix rather than equality, so a per-package tarball URL still maps
back to its originating repo:
It may also be worth having
get_package_source()build the archive URLdirectly from the
Repositoryfield when it is already a full tarball URL(as R-universe provides), or resolve the source via
utils::download.packages(),which handles R-universe
RepositoryURLs natively.Workaround
For anyone hitting this before a fix lands, the two internals can be patched in
place at the top of a check script:
Session info
checked0.5.4repos(e.g.https://<user>.r-universe.dev)