sessioncheck::sessioncheck(
action = "error",
checks = c("globalenv_objects", "attached_packages", "attached_environments"),
allow_globalenv_objects = c(
"narrow", "wide", "very_wide", "cache_images", "blog_dir", "theme_custom",
".main", ".QuartoInlineRender", ".Random.seed"
)
)There is an ancient saying.
Okay, well, now that I think about it maybe it’s not so ancient: it’s only been around since 2017 or thereabouts. And actually it’s more of kind of friendly warning than it is a saying. And really it’s only used by a weird group of people who write R scripts and spend too much time on the internet, and I don’t know if that’s wide enough usage to justify such a melodramatic opening. But nevertheless, the warning is real and it goes like this:
If your R script begins with rm(list=ls()), Jenny Bryan will come to your office and set your laptop on fire.
Among the wizened sages and those young folks who have read the old lore, this warning will be met with much nodding and its deep wisdom will need no further explanation. For in such company it is known that Jenny Bryan is not a malicious spirit who delights in the sight of laptops burning in the twilight, but is instead a kind and gentle teacher who wants you to understand the risks you are taking if you treat rm(list=ls()) as a magic incantation that will protect your analysis script from the eldritch terrors that lurk within the R session state.
Having now aged into that demographic bracket in which I am – rather inappropriately, I feel – considered one of the wizened R sages, I have lived so long with the deep truth of this warning that it is burned into my very soul. So much so that I have almost forgotten that there is a another deep and fundamental truth that must be respected, namely the xkcd 10000 rule:

In my online persona as “that middle aged trans lady who posts weird-ass R shit to the internet” I tend to move in circles where Jenny Bryan’s advice is well understood already, and in that capacity I never seem to run into any of the lucky 100001 who are learning this for the first time. But in my somewhat more professional persona as “a scientist who uses R to write data analysis scripts”, I am muuuuuuuch more likely to encounter people who have never been exposed to this particular truth.
With the benefit of hindsight, this is not surprising. The people who post a lot of #rstats content on social media websites – and, by extension, the readers of my silly blog – are a highly unrepresentative sample of R users. The vast majority of people who use R aren’t programmers. They probably aren’t statisticians either. In practice, most people who use R are everyday scientists and analysts who use it pragmatically, as a tool to quickly solve substantive problems they care about. The intricate details of “what the fuck is going on inside my R session” is not really something they care about. Not even a little bit. All they want is for it to “just work”. And… fair enough, actually. At its heart, R is a clunky statistical programming language that emphasises one thing above everything else: the user needs to get shit done, quickly.
It’s admirable really.
And so it is with these truths in mind that I started writing sessioncheck. Sessioncheck is an R package designed to provide a drop-in replacement for rm(list=ls()). It’s intended to do something useful out of the box, and to be just-customisable-enough that it can be adapted to a variety of different use cases. It is, however, designed explicitly for novice and intermediate level R users who write “human in the loop” style analysis scripts. It’s not meant to be used in production code,2 and if you’re an expert R user you probably already have better solutions in place. All of which is to say that if you are an experienced R user with deep understanding of the language, you don’t need sessioncheck. It’s not for you. It’s supposed to be a simple safety guardrail to assist folks who don’t have this rarefied knowledge.
The key idea
The central design principle behind sessioncheck is to serve as a drop-in replacement for rm(list=ls()). That is, an R user should be able to insert one simple line of code into their script that does something “spiritually similar” to what the user probably wants to accomplish by putting rm(list=ls()) in their script, but do it in a safer way. It needs to be exactly that simple. One line of code. Preferably short. And here it is:
sessioncheck::sessioncheck("error")Error: Session check results:
- Objects in global environment: blog_dir, cache_images, narrow, theme_custom, and 2 more
- Attached packages: [no issues]
- Attached environments: [no issues]
For an experienced R user, you can look at the output and see what this command does. It inspects the global environment and the search path, and throws an error if it detects anything “unexpected” in those places. In the context of this blog post, the check fails because there are a bunch of variables that I use when writing my blog posts (e.g., theme_custom is used when I create plots). They exist in this R session because I want them to be there, but you shouldn’t expect to see them in a “clean” R session!
Compared to rm(list=ls()), an out-of-the-box call to sessioncheck() has the following virtues:
- It checks attached packages, not just global environment variables
- It does not attempt to “clean up” a contaminated R session
Both of these strike me as critically important in the wild:
- The most common failure I see when data analysis scripts are run in a contaminated R session relates to the packages that the user has previously loaded. Variables in the global environment are actually less of an issue in my experience. Given that, it’s important that a drop-in replacement for
rm(list=ls())should look at the loaded packages. - Among the least desirable aspects to
rm(list=ls())is what it does when it detects a problem. Specifically it (a) doesn’t inform the user, and (b) it attempts to silently “fix” the problem by nuking global environment. That’s a recipe for disaster. What ought to happen instead is that (a) the user should be informed, and (b) responsibility for fixing the problem should be passed back to the human user. That’s whatsessioncheck()does.
Over and above this, sessioncheck() has other virtues:
- If called with
action = "error"as the first argument, it won’t let the script run if an error is detected. The script immediately fails and prompts the user to address the issue. - It is extensible. By default,
sessioncheck()runs a few “quick and dirty” checks of the R session, chosen to ensure that its behaviour is always safer thanrm(list=ls()), but the package comes with additional tools that make it possible to modify the checks that are performed. By providing this extra flexibility, my hope is that it would gently encourage new users to start thinking more deeply about what the R session state actually is, and making informed choices about the risks they’re willing to accept when running a script.
The details
Yes, yes, Danielle. I already understand these principles, could you possibly speed up this exposition and say a little more about what the bloody thing actually does? Your verbiage is not as interesting as you think it is?
Fair point.
Okay, here’s basic design. The sessioncheck package exports a collection of specific “check” functions. Each of these functions inspects a single aspect to the R session and reports back to the user if any concerns are detected. At present, the following checkers are available:
check_globalenv_objects()check_attached_packages()check_attached_environments()check_loaded_namespaces()check_sessiontime()check_required_options()check_required_locale()check_required_sysenv()
Each of these does pretty much what the function name suggests it does: check_globalenv_objects() looks at the global environment to see what objects it contains, check_attached_packages() takes a peek at the search path to find out what packages are attached, and so on. When the user calls sessioncheck(), some subset of these checks are performed, and the results are aggregated in a single message. You can customise exactly which checks are performed, and pass options to the specific checks if you want to whitelist certain packages or whatever. For instance, if I want the checks to ignore the variables that are always present in my blog posts I could do this:
Clearly, this is quite a bit more detailed and frankly I would not want to write this code over and over again, but thankfully I don’t have to. The package supports a “sessioncheck” option that you can set in the .Rprofile if you want. So I could totally put this in my .Rprofile file:
options(
sessioncheck = list(
action = "error",
checks = c("globalenv_objects", "attached_packages", "attached_environments"),
allow_globalenv_objects = c(
"narrow", "wide", "very_wide", "cache_images", "blog_dir", "theme_custom",
".main", ".QuartoInlineRender", ".Random.seed"
)
)
)Having done so, the only thing I’d need to do is this:
sessioncheck::sessioncheck()As an additional feature, sessioncheck() invisibly returns an object that is coercible to a data frame, so the user can easily get an overview of what the function actually checked, and whether or not the results of that check triggered an action:
as.data.frame(sessioncheck::sessioncheck()) type entity status
1 globalenv .main FALSE
2 globalenv .QuartoInlineRender FALSE
3 globalenv .Random.seed FALSE
4 globalenv blog_dir FALSE
5 globalenv cache_images FALSE
6 globalenv narrow FALSE
7 globalenv theme_custom FALSE
8 globalenv very_wide FALSE
9 globalenv wide FALSE
10 package stats FALSE
11 package graphics FALSE
12 package grDevices FALSE
13 package utils FALSE
14 package datasets FALSE
15 package methods FALSE
16 package base FALSE
17 attachment .GlobalEnv FALSE
18 attachment package:stats FALSE
19 attachment package:graphics FALSE
20 attachment package:grDevices FALSE
21 attachment package:utils FALSE
22 attachment package:datasets FALSE
23 attachment package:methods FALSE
24 attachment Autoloads FALSE
25 attachment package:base FALSE
To my mind, that’s a handy diagnostic. You can look at this and see that three types of checks were performed (the type column). You can see exactly which aspects to the R session were detected by each check (the entity column). And you can also see whether that specific aspect triggered an action (the status column).
What’s the point, Danielle?
At this point, you (the presumed experienced R user who already knows all this stuff forwards and backwards) might be wondering exactly why I bothered to write all this extensibility into sessioncheck. After all, the package is deliberately pitched at novices. Novice R users aren’t going to understand the difference between a loaded namespace and an attached package, so why the hell does sessioncheck bother to build that nuance into the check functions? Simialrly, a novice isn’t going to know precisely what an .Rprofile does, so why do I support the use of the "sessioncheck" option?
In practice, I don’t really expect that a novice R user will understand any of these details. They won’t immediately find themselves customising the .Rprofile for each project, and they most certainly won’t immediately start thinking about how to handle hidden dot-prefixed variables like .Random.seed when they appear in the global environment. The distiction between namespaces and attached package environments won’t be apparent to them for a long time. Indeed, if a novice user knew those things they wouldn’t be a novice anymore. If you have the capacity to work with those things fluently, you already have the skills you need to graduate to proper project-oriented workflows or (gods forbid) targets.
But also… that’s the point.
The whole idea behind sessioncheck is that it’s supposed to be scaffolding. A novice user can blndly use sessioncheck::sessioncheck() without thinking about the specifics, secure in the knowledge that it is strictly safer than doing the batshit rm(list=ls()) trick that gets passed around via folklore, but also it is extensible. The sessioncheck package has extra hooks (and documentation!) designed to guide novice users towards doing something even better than the defaults I’ve set. Eventually, if a user gets into the habit of thinking about what aspects of the session state need to be checked (and, ideally, disagreeing with my default choices!), they’ll find they’ve acquired all the skills they need to kick away the scaffold. They won’t need to use sessioncheck anymore because they’ll have discovered that there are more powerful tools they can use, and they’ll be ready to start learning them.
And so I come to the real point of the post…
I’m thinking of sending this package to CRAN, and I am thinking that once it’s there I will gently nudge people in my professional world towards using sessioncheck::sessioncheck() instead of rm(list=ls()). But… I don’t want to do this if there are any egregious flaws in my thinking, or if there are horrific bugs in the package that would actually make it worse than the – admittedly terrible – practice of using rm(list=ls()). So, dear reader, beloved R expert who reads my silly blog…
Is this sessioncheck package any good? Is it worth making this available on CRAN? If you have time to take a quick look at the documentation and/or code, I would welcome your thoughts, criticisms, and comments.
Because we are all in this together, and if there’s one thing we all care about it’s making sure that everyone can use R safely and freely, to solve whatever problems they personally care about.
And nobody wants to see another unnecessary laptop fire :)

Footnotes
Or whatever the much much smaller number would be for “R users learning something slightly non-obvious about R for the first time”↩︎
Dear lord in heaven no. If you’re putting R in prod you bloody well better be doing something a thousand times better than this. Analysis scripts can “get away” with simple heuristics precisely because they are one-and-done single use scripts that have human oversight over the outputs. If you’re letting code run free in a production environment and you don’t already have a stricter control in place than this one… um… hon… no.↩︎
Reuse
Citation
@online{navarro2026,
author = {Navarro, Danielle},
title = {Some Thoughts on Checking the {R} Session},
date = {2026-01-06},
url = {https://blog.djnavarro.net/posts/2026-01-06_sessioncheck/},
langid = {en}
}