From a5f93f11357219ea1ceb18aa9df0f20193ba6f3a Mon Sep 17 00:00:00 2001 From: jgabry Date: Thu, 9 Apr 2026 17:43:39 -0600 Subject: [PATCH 1/4] Informative error message if temp csv files are not found during Quarto rendering Fixes #1012 --- R/fit.R | 28 +++++++++++++++++++++++----- R/options.R | 3 +++ tests/testthat/test-fit-mcmc.R | 29 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/R/fit.R b/R/fit.R index bf60a0e9..c7d291f4 100644 --- a/R/fit.R +++ b/R/fit.R @@ -1432,11 +1432,29 @@ 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( - files = self$output_files(include_failed = FALSE), - variables = variables, - sampler_diagnostics = sampler_diagnostics, - format = format + csv_contents <- tryCatch( + read_cmdstan_csv( + files = self$output_files(include_failed = FALSE), + variables = variables, + sampler_diagnostics = sampler_diagnostics, + format = format + ), + 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 when using Quarto or Rmarkdown caching,\n", + " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" + ), + call. = FALSE + ) + } + stop(e) + } ) private$inv_metric_ <- csv_contents$inv_metric private$metadata_ <- csv_contents$metadata diff --git a/R/options.R b/R/options.R index 9e526ca7..902d2589 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 Rmarkdown or Quarto does not store files in 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/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index aede1110..cb31b32d 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 guidance for fits created with temp output", { + # 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 quarto caching + 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 when using Quarto or Rmarkdown caching,\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()) From d6995066d48c26ddb106507a806b753d7ebb94de Mon Sep 17 00:00:00 2001 From: jgabry Date: Thu, 9 Apr 2026 18:06:19 -0600 Subject: [PATCH 2/4] Apply changes to all model fitting methods --- R/fit.R | 73 ++++++++++++++++++++-------------- tests/testthat/test-fit-mcmc.R | 6 +-- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/R/fit.R b/R/fit.R index c7d291f4..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,29 +1453,11 @@ 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 <- tryCatch( - read_cmdstan_csv( - files = self$output_files(include_failed = FALSE), - variables = variables, - sampler_diagnostics = sampler_diagnostics, - format = format - ), - 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 when using Quarto or Rmarkdown caching,\n", - " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" - ), - call. = FALSE - ) - } - stop(e) - } + csv_contents <- private$read_cmdstan_csv_with_cache_hint_( + files = self$output_files(include_failed = FALSE), + variables = variables, + sampler_diagnostics = sampler_diagnostics, + format = format ) private$inv_metric_ <- csv_contents$inv_metric private$metadata_ <- csv_contents$metadata @@ -1922,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) @@ -2032,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) @@ -2115,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) @@ -2183,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) @@ -2301,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/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index cb31b32d..4b5e9445 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -90,7 +90,7 @@ 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 guidance for fits created with temp output", { +test_that("draws() errors with cached render guidance for fits created with temp output", { # https://github.com/stan-dev/cmdstanr/issues/1012 # https://github.com/stan-dev/cmdstanr/pull/1176 @@ -103,7 +103,7 @@ test_that("draws() errors with quarto cache guidance for fits created with temp 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 quarto caching + # that mentions cached Quarto or R Markdown renders. cmdstanr_output_dir = test_path("resources"), knitr.in.progress = TRUE )) @@ -112,7 +112,7 @@ test_that("draws() errors with quarto cache guidance for fits created with temp fit$draws(), paste0( "Assertion on 'files' failed: File does not exist: '", csv_files[[1]], "'.\n", - " If this error happened when using Quarto or Rmarkdown caching,\n", + " If this error happened during a cached Quarto or R Markdown render,\n", " see `cmdstanr_output_dir` in `?cmdstanr_global_options`" ), fixed = TRUE From bd2e8066b9db55d73e3fc391f82f49091fc37270 Mon Sep 17 00:00:00 2001 From: jgabry Date: Thu, 9 Apr 2026 18:10:01 -0600 Subject: [PATCH 3/4] update doc --- R/options.R | 2 +- man/cmdstanr_global_options.Rd | 3 +++ tests/testthat/test-fit-mcmc.R | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/R/options.R b/R/options.R index 902d2589..900d97d9 100644 --- a/R/options.R +++ b/R/options.R @@ -28,7 +28,7 @@ #' 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 in a +#' Note that using caching with Rmarkdown or Quarto does not store files from a #' temporary directory, so we recommend setting `cmdstanr_output_dir` to avoid #' failures when re-rendering. #' 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 4b5e9445..73666cd9 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -90,7 +90,7 @@ 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 cached render guidance for fits created with temp output", { +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 From 081677a953c001f1f4f0560f136af3199e04d213 Mon Sep 17 00:00:00 2001 From: Aki Vehtari Date: Sat, 11 Apr 2026 10:46:29 +0300 Subject: [PATCH 4/4] doc: Rmarkdown -> R Markdown --- R/options.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/options.R b/R/options.R index 900d97d9..1de6cd63 100644 --- a/R/options.R +++ b/R/options.R @@ -28,7 +28,7 @@ #' 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 +#' 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. #'