Skip to contents

Finds where an error or warning message comes from, without a language model and without the network: every message the packages raise is written in their code, so the code is searched. A message built from parts (a file name, a number) matches with those parts as wildcards; one that was cut short or retyped matches by its words. For each match you get the function that raises it, the exported functions it is reached from (their help pages say what they expect), and the message as written, which usually says what to do.

Usage

explerr(msg = geterrmessage(), pkgs = NULL, n = 3L)

Arguments

msg

The message: the text of an error, as R printed it (“Error in f(x) : ...” is fine), a condition object, or a try() result. Default: the last error of this session (geterrmessage()). Or a data frame of check rows with the columns check, ok and detail, as rmbl verify --json writes them and vrfcps returns them in checks: each failed row is explained.

pkgs

Character vector of installed packages to search. The default, NULL, means this package, or the comma-separated list in RMBL_PACKAGES: an environment variable that a package built on this one sets for its own command line.

n

How many matches to return, best first: a whole number, default 3.

Value

A data frame with at most n rows, best match first, and the character columns package, raised_by (the function whose code raises it), exported (the exported functions that reach it, separated by commas), kind ("error" or "warning"), message (the message as written, with ... where a value goes) and match ("exact" or the share of its words found, such as "80% of its words"). No rows when the message is not one of these packages' own: it came from another package or from R, and llmask can help with it. For a data frame of check rows: one row per failed check, with the columns check, detail, explanation (what the failure means and what to do) and raised_by (pkg::fn when the detail is a message of these packages, else NA).

Details

The first call in a session walks the body of every function in the searched packages and collects each stop(), warning() and usage error whose literal text has at least 12 letters. A message built with sprintf(), gettextf(), paste() or paste0() becomes a template with its computed parts as wildcards. Each raising function is traced up to four calls to the exported functions that reach it (at most six are listed). The table is kept in memory for the session (about a second to build for this package), and, while a person is watching an interactive session and the answer cache is on, also saved as an .rds file in the answer-cache directory.

The answer-cache directory is the one tools::R_user_dir() gives for "rmoriebricklayer" and "cache", or the one the rmoriebricklayer.llmcachedir option names when it is set. The cache is off under options(morie.quiet = TRUE), MORIE_LLM_CACHE=off, tests and R CMD check.

The message is cleaned first: an “Error in f(x) :” or “Warning message:” prefix is dropped, curly quotes become straight ones and white space is collapsed. A template that matches the start of the message as a regular expression is an exact match and ranks first (longer templates before shorter ones). Otherwise a template scores \(0.6 h / |m| + 0.4 h / |t|\), where \(h\) is the number of shared words (three letters or more, common words left out, at least two needed), \(|m|\) the words of the message and \(|t|\) those of the template; scores of 0.55 and above are kept.

Errors: msg that is not character text, a condition or a data frame with the columns check, ok and detail; n not a whole number of 1 or more; pkgs that are not installed.

Examples

msg <- "`prompt` must be a single non-empty string"
explerr(msg, pkgs = "rmoriebricklayer")
#>            package         raised_by
#> 1 rmoriebricklayer     .rmbl_string1
#> 2 rmoriebricklayer     .bl_tmpl_name
#> 3 rmoriebricklayer .rmbl_fips_scheme
#>                                                        exported  kind
#> 1            llmask, agentbndl, anltab, arccap, arcfind, arcget error
#> 2                                    blcli, rmblapp, usecpstmpl error
#> 3 blcli, capsule_sign, cpsatst, cpsbndlvrf, cpschkatst, cpssign error
#>                                      message            match
#> 1    `...` must be a single non-empty string            exact
#> 2   `name` must be a single non-empty string 80% of its words
#> 3 `scheme` must be a single non-empty string 80% of its words

# a condition object
e <- tryCatch(kemsz(3), error = identity)
explerr(e, n = 1)
#>            package       raised_by                         exported  kind
#> 1 rmoriebricklayer .rmbl_kem_level kemkg, kemsz, kemdecap, kemencap error
#>                                 message match
#> 1 `...` must be one of 512, 768 or 1024 exact

# the failed rows of a capsule check
rows <- data.frame(
  check = c("data_present", "data_sha256"),
  ok = c(TRUE, FALSE),
  detail = c("d.csv", "expected 01..., got 9c...")
)
explerr(rows)$explanation
#> [1] "the data file's bytes differ from the pinned SHA-256: restore the file, or pin the new bytes with cpsrfrsh() (rmbl refresh)"

# n must be at least 1
try(explerr(msg, n = 0))
#> Error : `n` must be a whole number of 1 or more
if (FALSE) { # interactive()
# an error caught with try(), and the message as R prints it, with the call
# in front
r <- try(llmcfg(no.such = 1), silent = TRUE)
explerr(r, pkgs = "rmoriebricklayer")
explerr(
  paste0(
    "Error in mrkprf(x, 9) : `index` must be between 1 and the number ",
    "of chunks"
  ),
  n = 1
)

# a message retyped from memory
explerr("index between number chunks", n = 1)

# R's own messages are not in the table: no rows
nrow(explerr("object 'x' not found"))
}