Skip to content

Commit 2be99fa

Browse files
committed
Better handle errors in options(error = )
1 parent af4fe47 commit 2be99fa

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

crates/ark/src/interface.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,14 +1465,14 @@ impl RMain {
14651465
/// https://github.com/rstudio/renv/blob/5d0d52c395e569f7f24df4288d949cef95efca4e/inst/resources/activate.R#L85-L87
14661466
fn handle_invalid_input_request(&self, buf: *mut c_uchar, buflen: c_int) -> ConsoleResult {
14671467
if let Some(input) = Self::renv_autoloader_reply() {
1468-
log::info!("Detected `readline()` call in renv autoloader. Returning `'{input}'`.");
1468+
log::warn!("Detected `readline()` call in renv autoloader. Returning `'{input}'`.");
14691469
match Self::on_console_input(buf, buflen, input) {
14701470
Ok(()) => return ConsoleResult::NewInput,
14711471
Err(err) => return ConsoleResult::Error(format!("{err}")),
14721472
}
14731473
}
14741474

1475-
log::info!("Detected invalid `input_request` outside an `execute_request`. Preparing to throw an R error.");
1475+
log::warn!("Detected invalid `input_request` outside an `execute_request`. Preparing to throw an R error.");
14761476

14771477
let message = vec![
14781478
"Can't request input from the user at this time.",

crates/ark/src/modules/positron/errors.R

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,33 @@ invoke_option_error_handler <- function() {
201201
}
202202

203203
for (hnd in handler) {
204-
eval(hnd, globalenv())
204+
err <- tryCatch(
205+
{
206+
eval(hnd, globalenv())
207+
NULL
208+
},
209+
error = identity
210+
)
211+
212+
if (!is.null(err)) {
213+
# Disable error handler to avoid cascading errors
214+
options(error = NULL)
215+
216+
# We don't let the error propagate to avoid a confusing sequence of
217+
# error messages from R, such as "Error during wrapup"
218+
writeLines(
219+
c(
220+
"The `getOption(\"error\")` handler failed.",
221+
"This option was unset to avoid cascading errors.",
222+
"Caused by:",
223+
conditionMessage(err)
224+
),
225+
con = stderr()
226+
)
227+
228+
# Bail early
229+
return()
230+
}
205231
}
206232
}
207233

crates/ark/tests/kernel.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,41 @@ fn test_env_vars() {
820820
assert_eq!(frontend.recv_shell_execute_reply(), input.execution_count);
821821
}
822822

823+
#[test]
824+
fn test_execute_request_error_handler_failure() {
825+
let frontend = DummyArkFrontend::lock();
826+
827+
// Define the functions and set the error handler
828+
let code = r#"
829+
f <- function() g()
830+
g <- function() h()
831+
h <- function() stop("foo")
832+
options(error = function() stop("ouch"))
833+
"#;
834+
frontend.execute_request_invisibly(code);
835+
836+
frontend.send_execute_request("f()", ExecuteRequestOptions::default());
837+
frontend.recv_iopub_busy();
838+
let input = frontend.recv_iopub_execute_input();
839+
assert_eq!(input.code, "f()");
840+
841+
frontend.recv_iopub_stream_stderr(
842+
r#"The `getOption("error")` handler failed.
843+
This option was unset to avoid cascading errors.
844+
Caused by:
845+
ouch
846+
"#,
847+
);
848+
849+
assert!(frontend.recv_iopub_execute_error().contains("foo"));
850+
851+
frontend.recv_iopub_idle();
852+
assert_eq!(
853+
frontend.recv_shell_execute_reply_exception(),
854+
input.execution_count
855+
);
856+
}
857+
823858
/// Install a SIGINT handler for shutdown tests. This overrides the test runner
824859
/// handler so it doesn't cancel our test.
825860
fn install_sigint_handler() {

0 commit comments

Comments
 (0)