diff --git a/R/fit.R b/R/fit.R index bf60a0e9..7e0053a3 100644 --- a/R/fit.R +++ b/R/fit.R @@ -95,7 +95,28 @@ CmdStanFit <- R6::R6Class( init_ = NULL, profiles_ = NULL, model_methods_env_ = NULL, - return_codes_ = NULL + return_codes_ = NULL, + read_cmdstan_csv_with_cache_hint_ = function(...) { + tryCatch( + read_cmdstan_csv(...), + error = function(e) { + err_msg <- conditionMessage(e) + if (isTRUE(getOption("knitr.in.progress")) && + isTRUE(self$runset$args$using_tempdir) && + grepl("File does not exist:", err_msg, fixed = TRUE)) { + stop( + paste0( + err_msg, + "\n If this error happened during a cached Quarto or R Markdown render,\n", + " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" + ), + call. = FALSE + ) + } + stop(e) + } + ) + } ) ) @@ -1432,7 +1453,7 @@ CmdStanMCMC <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("No chains finished successfully. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv( + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( files = self$output_files(include_failed = FALSE), variables = variables, sampler_diagnostics = sampler_diagnostics, @@ -1904,7 +1925,10 @@ CmdStanMLE <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Optimization failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$point_estimates private$metadata_ <- csv_contents$metadata invisible(self) @@ -2014,7 +2038,10 @@ CmdStanLaplace <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Laplace inference failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$draws private$metadata_ <- csv_contents$metadata invisible(self) @@ -2097,7 +2124,10 @@ CmdStanVB <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Variational inference failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$draws private$metadata_ <- csv_contents$metadata invisible(self) @@ -2165,7 +2195,10 @@ CmdStanPathfinder <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Pathfinder failed. Unable to retrieve the draws.", call. = FALSE) } - csv_contents <- read_cmdstan_csv(self$output_files(), format = format) + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( + files = self$output_files(), + format = format + ) private$draws_ <- csv_contents$draws private$metadata_ <- csv_contents$metadata invisible(self) @@ -2283,7 +2316,7 @@ CmdStanGQ <- R6::R6Class( if (!length(self$output_files(include_failed = FALSE))) { stop("Generating quantities for all MCMC chains failed. Unable to retrieve the generated quantities.", call. = FALSE) } - csv_contents <- read_cmdstan_csv( + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( files = self$output_files(include_failed = FALSE), variables = variables, sampler_diagnostics = "", diff --git a/R/options.R b/R/options.R index 9e526ca7..1de6cd63 100644 --- a/R/options.R +++ b/R/options.R @@ -28,6 +28,9 @@ #' CSV files when fitting models. The default is a temporary directory. Files in #' a temporary directory are removed as part of \R garbage collection, while #' files in an explicitly defined directory are not automatically deleted. +#' Note that using caching with R Markdown or Quarto does not store files from a +#' temporary directory, so we recommend setting `cmdstanr_output_dir` to avoid +#' failures when re-rendering. #' #' * `cmdstanr_verbose`: Should more information be printed #' when compiling or running models, including showing how CmdStan was called diff --git a/man/cmdstanr_global_options.Rd b/man/cmdstanr_global_options.Rd index 04e08bd5..33827ad9 100644 --- a/man/cmdstanr_global_options.Rd +++ b/man/cmdstanr_global_options.Rd @@ -25,6 +25,9 @@ CmdStan be disabled? The default is \code{FALSE}. CSV files when fitting models. The default is a temporary directory. Files in a temporary directory are removed as part of \R garbage collection, while files in an explicitly defined directory are not automatically deleted. +Note that using caching with Rmarkdown or Quarto does not store files from a +temporary directory, so we recommend setting \code{cmdstanr_output_dir} to avoid +failures when re-rendering. \item \code{cmdstanr_verbose}: Should more information be printed when compiling or running models, including showing how CmdStan was called internally? The default is \code{FALSE}. diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index aede1110..73666cd9 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -90,6 +90,35 @@ test_that("draws() method returns draws_array (reading csv works)", { expect_equal(posterior::variables(draws_beta_alpha), c("beta[1]", "beta[2]", "beta[3]", "alpha")) }) +test_that("draws() errors with Quarto cache message when temp files are missing", { + # https://github.com/stan-dev/cmdstanr/issues/1012 + # https://github.com/stan-dev/cmdstanr/pull/1176 + + fit <- testing_fit("logistic", method = "sample", seed = 123, chains = 1) + csv_files <- fit$output_files() + + # Simulate a later cached re-render: the fit object still points to the temp + # output files, which don't exist anymore + unlink(csv_files, force = TRUE) + withr::local_options(list( + # Even if cmdstanr_output_dir is now set to a non-temp directory, + # it was not set when the fit was created so we should still get the error message + # that mentions cached Quarto or R Markdown renders. + cmdstanr_output_dir = test_path("resources"), + knitr.in.progress = TRUE + )) + + expect_error( + fit$draws(), + paste0( + "Assertion on 'files' failed: File does not exist: '", csv_files[[1]], "'.\n", + " If this error happened during a cached Quarto or R Markdown render,\n", + " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" + ), + fixed = TRUE + ) +}) + test_that("inv_metric() method works after mcmc", { x <- fit_mcmc_1$inv_metric() expect_length(x, fit_mcmc_1$num_chains())