Skip to content

rlang::cnd_entrace(cnd) assumes that 'cnd' can be subsetted with [[ and .subset2(), which I don't think it true #1841

@HenrikBengtsson

Description

@HenrikBengtsson

Reading help("condition"), there's nothing saying that a condition object should be a list. For example, there's in nothing in the R documentation that says the following is a invalid condition object:

cnd2 <- structure(42, class = "condition")

My interpretation of the help page is that it is written such that carefully avoids declaring what type a condition object should be. In contrast, other help pages are often explicit about the type, e.g. a "a list" or "a named list", etc. I asked about this in the R-devel thread "Is structure(NA, class = c("def", "condition")) a valid 'condition' object?" on 2025-10-07 (https://stat.ethz.ch/pipermail/r-devel/2025-October/084184.html) - there's only one reply thus far, which seems to agree with my interpretation.

Looking at the implementation of rlang::cnd_entrace(), it appears that it assumes that condition:s are of type list, or possible, environment, because it makes use of [[ and .subset2(). For example, this works:

cnd <- structure(list(), class = "condition")
cnd <- rlang::cnd_entrace(cnd)

but not:

cnd <- structure(42, class = "condition")
cnd <- rlang::cnd_entrace(cnd)
#> Error in x[["trace"]] : subscript out of bounds
#> 
#> 1: rlang::cnd_entrace(cnd)
#> 2: cnd_some(cnd, function(x) !is_null(x[["trace"]]))

Workaround

I think names() is defined for all types in R. This could be used to gate-keep the assumptions made by rlang, e.g.

diff --git a/R/cnd-entrace.R b/R/cnd-entrace.R
index 50ab54ea9..f64188e83 100644
--- a/R/cnd-entrace.R
+++ b/R/cnd-entrace.R
@@ -239,7 +239,7 @@ has_recover <- function() {
 cnd_entrace <- function(cnd, ..., top = NULL, bottom = NULL) {
   check_dots_empty0(...)
 
-  if (cnd_some(cnd, function(x) !is_null(x[["trace"]]))) {
+  if (cnd_some(cnd, function(x) "trace" %in% names(x) && !is_null(x[["trace"]]))) {
     return(cnd)
   }
 
diff --git a/R/cnd.R b/R/cnd.R
index 6b9286020..fb3d88494 100644
--- a/R/cnd.R
+++ b/R/cnd.R
@@ -381,7 +381,8 @@ cnd_some <- function(cnd, fn, ...) {
       return(TRUE)
     }
 
-    inherit <- .subset2(.subset2(cnd, "rlang"), "inherit")
+    inherit <- ("rlang" %in% names(cnd)) &&
+               .subset2(.subset2(cnd, "rlang"), "inherit")
     if (is_false(inherit)) {
       return(FALSE)
     }

There might be more instances in rlang where using $, [[, and .subset2() on condition need to be conditioned as above.

Background

FWIW, my R.oo package has an Exception class that inherits from error and condition is for this discussion effectively defined as:

cnd <- structure(NA, class = c("Exception", "error", "condition"))

This Exception class is how many of the error have been thrown in R.oo, R.utils, R.filesets, aroma.affymetrix, ... since ~2001. I became aware of this problem after a recent issue report (HenrikBengtsson/R.utils#161).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions