I am having some troubles in understanding how sessions work in the shiny-server. I assume that a session finishes when the user close the browser, however, by using the print(session$isClosed())
in the server function I get a FALSE
response at the beginning (so okay) and then when I close the browser nothing happens. Can anyone give me a clue about shiny-server sessions? I would want to store session specific plots to let the users download their plots only.

- 25,502
- 4
- 67
- 139

- 1,149
- 2
- 12
- 18
-
If I'm not wrong, you would like to let users download plots and data etc pertaining to the current session only, and not from any previous session, yeah? – Sumax Feb 24 '20 at 07:15
1 Answers
Well, to start with a shiny session object is a specific ('R6') data structure in shiny, made of public and private elements. It's purpose is to record one instance of the relationship between one user and shiny (more on this later).
>str(session)
Classes 'ShinySession', 'R6' <ShinySession>
Public:
@uploadEnd: function (jobId, inputId)
@uploadieFinish: function ()
@uploadInit: function (fileInfos)
allowReconnect: function (value)
clientData: reactivevalues
clone: function (deep = FALSE)
close: function ()
closed: FALSE
decrementBusyCount: function ()
defineOutput: function (name, func, label)
dispatch: function (msg)
doBookmark: function ()
downloads: Map, R6
exportTestValues: function (..., quoted_ = FALSE, env_ = parent.frame())
files: Map, R6
fileUrl: function (name, file, contentType = "application/octet-stream")
flushOutput: function ()
freezeValue: function (x, name)
getBookmarkExclude: function ()
getTestEndpointUrl: function (inputs = TRUE, outputs = TRUE, exports = TRUE, format = "rds")
groups: NULL
handleRequest: function (req)
incrementBusyCount: function ()
initialize: function (websocket)
input: reactivevalues
isClosed: function ()
isEnded: function ()
makeScope: function (namespace)
manageHiddenOutputs: function ()
manageInputs: function (data)
ns: function (id)
onBookmark: function (fun)
onBookmarked: function (fun)
onEnded: function (endedCallback)
onFlush: function (flushCallback, once = TRUE)
onFlushed: function (flushedCallback, once = TRUE)
onInputReceived: function (callback)
onRestore: function (fun)
onRestored: function (fun)
onSessionEnded: function (sessionEndedCallback)
output: shinyoutput
outputOptions: function (name, ...)
progressStack: environment
reactlog: function (logEntry)
registerDataObj: function (name, data, filterFunc)
registerDownload: function (name, filename, contentType, func)
reload: function ()
request: environment
resetBrush: function (brushId)
restoreContext: RestoreContext, R6
rootScope: function ()
saveFileUrl: function (name, data, contentType, extra = list())
sendBinaryMessage: function (type, message)
sendCustomMessage: function (type, message)
sendInputMessage: function (inputId, message)
sendInsertUI: function (selector, multiple, where, content)
sendModal: function (type, message)
sendNotification: function (type, message)
sendProgress: function (type, message)
sendRemoveUI: function (selector, multiple)
session: active binding
setBookmarkExclude: function (names)
setShowcase: function (value)
showProgress: function (id)
singletons:
token: d44d583f13b3cd4ccce43f59fe410f61
unhandledError: function (e)
updateQueryString: function (queryString)
user: NULL
wsClosed: function ()
Private:
.clientData: ReactiveValues, R6
.input: ReactiveValues, R6
.outputOptions: list
.outputs: list
bookmarkCallbacks: environment
bookmarkedCallbacks: environment
bookmarkExclude:
busyCount: 2
closedCallbacks: environment
createBookmarkObservers: function ()
enableTestEndpoint: function ()
fileUploadContext: environment
flushCallbacks: environment
flushedCallbacks: environment
getOutputOption: function (outputName, propertyName, defaultValue)
inputMessageQueue: list
inputReceivedCallbacks: environment
invalidatedOutputErrors: Map, R6
invalidatedOutputValues: Map, R6
outputValues: list
progressKeys: character
registerSessionEndCallbacks: function ()
restoreCallbacks: environment
restoredCallbacks: environment
sendErrorResponse: function (requestMsg, error)
sendMessage: function (...)
sendResponse: function (requestMsg, value)
shouldSuspend: function (name)
showcase: FALSE
storeOutputValues: function (values = NULL)
testEndpointUrl: session/d44d583f13b3cd4ccce43f59fe410f61/dataobj/shinyte ...
testValueExprs: list
websocket: WebSocket
write: function (json)
A good way to explore the session object is to play with the shiny example in shiny gallery client-data-and-query-string. It allows to see
what is contained for example in session$clientdata
or any other element of the object.
A couple of additional & misleadingly trivial points:
- when does a session starts? When a user connects with the shiny app
- when does a session ends? when a user disconnects with the shiny app
As an example, to show how the issue is actually quite complex, if I refresh the browser, I end the present session and create a new one.
Coming to session$isClosed()
, this is not the right function to connect to specific action when a session is ended.
This is actually the role of a shiny call back function
onSessionEnded(fun, session = getDefaultReactiveDomain())
A minimal example could be the following:
library(shiny)
ui =(
fluidPage(
titlePanel("This is an example")
)
)
server = function(input, output, session){
session$onSessionEnded({
print("Stop!")
stopApp
})
}
runApp(list(ui = ui, server = server))
If you try, refreshing (or breaking up with browser() ) will print "Stop" and will stop the app.
26 September 2017 Edit:
In general, I think it is better to be cautious if the continuity of a session is of importance (and in any case it is appropriate to test session
code directly on Shiny Server
or Shiny Server Pro
). Possibly the most important use cases come with Shiny Server Pro
, where any disconnection may
affect login status etc.).
I'm also aware that the shiny
team has made changes on these areas in recent versions. E.g., it seems that while onSessionEnded
still works, possibly it is not anymore the best function for this usecase .
See the following code as an example (from shiny
reference guide), using onStop
, that can work when a session ends, as well as when the app stops.
library(shiny)
cat("Doing application setup\n")
onStop(function() {
cat("Doing application cleanup\n")
})
shinyApp(
ui = basicPage("onStop demo"),
server = function(input, output, session) {
onStop(function() cat("Session stopped\n"))
}
)

- 2,543
- 1
- 25
- 38
-
Are you sure about this statement: "If I debug a shiny app with browser(), when the app is interrupted at the break, it ends the current session." ? I don't think that's true. Also, the `print("Stop!")` in the sample code is misleading because it doesn't run when the session ends, it runs when the session starts, only the `stopApp` callback is called when the session ends. I believe some of this information is wrong. – DeanAttali Sep 25 '17 at 16:03
-
I think `shiny` changed some time back the behaviour of when a `session` is interrupted (I will add a warning in the post). I re-tested whether `session#token` changes during debug with `browser()` and indeed the token remains the same. I think @Dean Attali comment is also a bit wrong: according to shiny ref. "`onSessionEnded` registers a function to be called after the client has disconnected", which to me means that the function is actually `run` after disconnection. E.g. in the past I used `session$onSessionEnded` to save files with some status of the app or to disconnect from a db. – Enzo Sep 26 '17 at 11:02
-
1`onSessionEnded()` does register a function callback, but what I said is still correct. Try it out: the print statement runs immediately on start. The reason that happens is because `onSessionEnded()` expects a **function**, but you passed it an **expression** that contained a print statement and then a function. So that second line, the `stopApp`, is the function that gets called when the session ends. But the code inside the expression gets run immediately. To get the behaviour you want I think you'll need `onSessionEnded(function(){print(...);stopApp())`. – DeanAttali Sep 26 '17 at 18:31
-
I tested with an old laptop using shiny 0.13.0 (Jan 2016) and the session did not change before/after a `browser()`. I really don't think it ever did that, a session only ends when the app dies, when the app timeouts, or when the user leaves the page (for example by closing the tab or refreshing it). But I am curious to see why you're getting the print to run on every time browser is used. Can you share the code for that? It makes sense that print runs once, but if you somehow made it so that it `onSessionEnded()` runs on every browser, then I'd be surprised (and admit my wrongness :) – DeanAttali Sep 26 '17 at 18:39
-
Is it possible to keep in session selected value of the "radioButtons" component? For example, I selected the 2nd element when default is the 1st element, and then during the current session would like to use it as default until it changed. – Andrii Feb 09 '19 at 07:35
-
1I have been searching all over stackoverflow and rstudio on any clarity on `onStop` and `onSessionEnded()` but am getting more and more confused with every post I read. Is there a good place where I can check how to close the app elegantly and perform some functions on closing? – Lazarus Thurston Aug 09 '21 at 17:00
-
I have a shiny app written on modules, some of them returning promises run in other R processes. Notifications wrapped in these promises do not appear on screen. Is there any way to use `session` to bring these notifications on screen? Thank you! – Dragos Bandur Aug 19 '23 at 18:17