From ba5b9612f4f70b2cb31d5789a12b3f6350bab171 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Mon, 29 Jun 2026 09:10:24 -0700 Subject: [PATCH 1/2] fix(plot): avoid leaking plot_backend into the global environment profile.R is sourced as the user's R_PROFILE_USER, so the top-level plot_backend assignment created a stray variable in the user's global environment that persisted after startup (reported by @Fred-Wu). Wrap the sess::connect() setup in local() so the temporary plot_backend binding stays scoped, matching the pattern used by the .Rprofile block above it. --- R/profile.R | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/R/profile.R b/R/profile.R index 4e441312..154ae87f 100644 --- a/R/profile.R +++ b/R/profile.R @@ -25,10 +25,12 @@ local({ }) if (requireNamespace("sess", quietly = TRUE)) { - plot_backend <- Sys.getenv("SESS_PLOT_BACKEND", "auto") - sess::connect( - use_rstudioapi = as.logical(Sys.getenv("SESS_RSTUDIOAPI", "TRUE")), - use_httpgd = (plot_backend %in% c("auto", "httpgd")), - use_jgd = (plot_backend %in% c("auto", "jgd")) - ) + local({ + plot_backend <- Sys.getenv("SESS_PLOT_BACKEND", "auto") + sess::connect( + use_rstudioapi = as.logical(Sys.getenv("SESS_RSTUDIOAPI", "TRUE")), + use_httpgd = (plot_backend %in% c("auto", "httpgd")), + use_jgd = (plot_backend %in% c("auto", "jgd")) + ) + }) } From 53abf19a3cc3bb80c5adebe04e579b29c241abcc Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Mon, 29 Jun 2026 09:50:23 -0700 Subject: [PATCH 2/2] feat(plot): warn when a requested plot backend is unavailable When r.plot.backend explicitly selects jgd or httpgd (or the legacy r.plot.useHttpgd is true) but the package is not installed, register_hooks silently fell back to the standard viewer. The standard viewer has no plot-cycling UI, so users saw a degraded experience with no explanation (several reports on #1706). Emit a warning from register_hooks naming the requested backend and the remedy, but only when a specific backend was explicitly requested. The auto mode (use_jgd && use_httpgd) still degrades silently by design. --- sess/R/hooks.R | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sess/R/hooks.R b/sess/R/hooks.R index ff114e32..75a7c9c9 100644 --- a/sess/R/hooks.R +++ b/sess/R/hooks.R @@ -162,6 +162,26 @@ register_hooks <- function(use_rstudioapi = TRUE, use_httpgd = TRUE, use_jgd = F notify_client("httpgd", list(url = httpgd::hgd_url())) }) } else { + # If a specific interactive backend was explicitly requested but is + # unavailable, warn before silently degrading to the standard viewer. + # (use_jgd && use_httpgd means "auto", which is meant to degrade quietly.) + if (xor(use_jgd, use_httpgd)) { + if (use_jgd && !requireNamespace("jgd", quietly = TRUE)) { + warning("[sess] Plot backend \"jgd\" was requested but the jgd package ", + "is not installed. Falling back to the standard plot viewer. ", + "Install jgd, or change the r.plot.backend setting.", call. = FALSE) + } else if (use_jgd) { + warning("[sess] Plot backend \"jgd\" was requested but no renderer ", + "connection is available. Falling back to the standard plot ", + "viewer.", call. = FALSE) + } else if (use_httpgd) { + warning("[sess] Plot backend \"httpgd\" was requested but the httpgd ", + "package is not installed. Falling back to the standard plot ", + "viewer. Install httpgd, or change the r.plot.backend setting.", + call. = FALSE) + } + } + # Default to static plot capturing (Re-implementation based on legacy plot handler) plot_file <- .sess_env$latest_plot_path file.create(plot_file, showWarnings = FALSE)