# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
12 Debugging apps
Debugging Shiny applications usually requires using an interactive debugger,
Capturing reactive values with reactiveValuesToList()
and sending output to the UI. The benefits of print debugging is that it’s easy to implement, doesn’t require any special tools or setup, and it provides a direct view of variable states and program flow at specific points.
One of the best tried and tested methods of debugging is simply adding a cat()
or print()
call somewhere in your code to print variables or objects to the R console. This is a basic but effective way to track variable changes.
In mod_var_input
:
- Place a
verbatimTextOutput()
in the ui function.
code("module reactive values"),
verbatimTextOutput(outputId = ns("mod_vals"))
)
- 1
-
Optional label
- 2
-
Include the
ns()
for theinputId
- In a
renderPrint()
, usereactiveValuesToList()
to gather theinputId
s and pass them toprint()
(I’m actually usinglobstr::tree()
to give a clearer display).
$mod_vals <- renderPrint({
output::tree(
lobstrreactiveValuesToList(
x = input,
all.names = TRUE
)
) })
- 1
-
Collect reactive values in module
- 2
-
Print these values to the UI
- 3
- Include all reactive objects
Load the package and run the app:
Ctrl/Cmd + Shift + L
ℹ Loading sap
launch_app(options = list(test.mode = FALSE), run = 'p')
Now we can see the reactive values from our module in the application sidebar!
We can also use this ‘print’ method to explore reactive values at various locations in our application. For example, if we wanted to print the reactive values for multiple modules in an app, we can use these methods in the top level movies_ui()
and movies_server()
functions.
In the bslib
portion of movies_ui()
:
Add
verbatimTextOutput()
with an optional labelcode("reactive values"), verbatimTextOutput(outputId = 'vals') )
In movies_server()
:
Collect all the
inputId
s withreactiveValuesToList()
and print withprint()
orlobstr::ast()
<- reactive({ all_vals reactiveValuesToList(x = input, all.names = TRUE) }) $vals <- renderPrint({ output::tree(all_vals()) lobstr })
Load the package and run the app:
Ctrl/Cmd + Shift + L
ℹ Loading sap
launch_app(options = list(test.mode = FALSE),
run = 'p', bslib = TRUE)
reactiveValuesToList()
printed from movies_ui()
and movies_server()
Here we can see both levels of reactive values (from the module and the UI/server functions). The handy thing about this method is that the values change when we interact with the application:
y
and vars-y
both update when the UI inputs change
Launch app with the shinypak
package:
launch('25.0_debug-error')
12.1 Debugging modules
The contents of your Shiny app-package can quickly become a complicated and intertwined combination of functions: utility, modules, UI, server, etc. I like to display the relationship between the functions with abstract syntax trees:1
For example, we know scatter_plot()
is called from within the scatter plot display module function:
█─mod_scatter_display_server
└─█─scatter_plot
And mod_scatter_display_server()
is called within movies_server()
:
█─movies_server
├─█─mod_scatter_display_server
│ └─█─scatter_plot
└─█─mod_var_input_server
Which is called from inside sap
:
█─launch_app
├─█─movies_ui
│ ├─█─mod_var_input_ui
│ └─█─mod_scatter_display_ui
└─█─movies_server
├─█─mod_scatter_display_server
│ └─█─scatter_plot
└─█─mod_var_input_server
I find these abstract folder trees helpful when I’m debugging or testing Shiny functions. I can use them to try and anticipate the application call stack (especially when I end up with multiple utility functions or nested modules).
We’ll add browser()
and observe()
in the movies_server()
function to capture the behaviors of both modules:
Launch app with the shinypak
package:
launch('25.1_debug-selected_vars')
<- function(input, output, session) {
movies_server
observe({
browser()
<- mod_var_input_server("vars")
selected_vars
mod_scatter_display_server("plot", var_inputs = selected_vars)
})
}
- 1
-
Observer scope
- 2
- Activate debugger
Then we’ll load the package and display the app in the Viewer pane (below the Console):
Ctrl/Cmd + Shift + L
ℹ Loading sap
launch_app(options = list(test.mode = FALSE), run = 'p')
ℹ shinyViewerType set to pane
The application launches, but browser()
pauses the execution of the modules and activates the IDE’s debugger. This allows us to view the objects that are available in movies_server()
before the variables are passed to the graph rendering functions:
observe(browser())
suspends the execution of any subsequent code
In the Source pane, we can see the call to browser()
highlighted (Browse[1]>
tells us the location in the browser()
function).
browser()
was called inside observe()
, the execution will pause, and we can interactively examine values
In the debugger, we want to confirm the returned values from the variable input module, selected_vars
, which requires us to execute the next two lines of code:
1]> n
Browse[2]> n Browse[
selected_vars
Inside movies_server()
:
mod_var_input_server()
collects the following values and returns a reactive list (selected_vars
):
- Three variable names
x
,y
,z
- Graph aesthetics
alpha
andsize
- An optional plot title
plot_title
When we inspect selected_vars
in the debugger console (without parentheses) we see the method (i.e., the reactive list of inputs), and not the actual values:
2]> selected_vars Browse[
reactive({
list(y = input$y, x = input$x,
z = input$z, alpha = input$alpha,
size = input$size,
plot_title = input$plot_title)
})
If we check selected_vars()
(with parentheses) in the debugger, we see this contains the values from the variable input module:
2]> selected_vars() Browse[
$y
[1] "audience_score"
$x
[1] "imdb_rating"
$z
[1] "mpaa_rating"
$alpha
[1] 0.5
$size
[1] 2
$plot_title
[1] ""
These two steps confirm that the UI values are being collected by the variable input module and stored in selected_vars
, so the error must be coming from inside the scatter plot display module.
12.2 Module communication
Launch app with the shinypak
package:
launch('25.2_debug-var_inputs')
We’ll repeat a similar process in mod_scatter_display_server()
, but include the call to observe(browser())
after moduleServer()
. Then we’ll load the package and run the application again:
<- function(id, var_inputs) {
mod_scatter_display_server moduleServer(id, function(input, output, session) {
observe({
browser()
# module code
})
}) }
- 1
-
Wrap
browser()
inobserve()
and place after the call tomoduleServer()
Ctrl/Cmd + Shift + L
ℹ Loading sap
launch_app(options = list(test.mode = FALSE), run = 'p')
Inside the module, we want to confirm var_inputs()
is being created correctly from the var_inputs
object in movies_server()
.
selected_vars
is the input for mod_scatter_display_server()
(as var_inputs
)
var_inputs
is converted to the reactiveinputs
inputs
is passed toscatter_plot()
insiderenderPlot()
2]> var_inputs() Browse[
$y
[1] "audience_score"
$x
[1] "imdb_rating"
$z
[1] "mpaa_rating"
$alpha
[1] 0.5
$size
[1] 2
$plot_title
[1] ""
12.2.1 Verify variable inputs
Inside the scatter plot display module, the var_inputs
argument is used to create a reactive input()
object for the graph created by scatter_plot()
:
<- reactive({
inputs <- tools::toTitleCase(var_inputs()$plot_title)
plot_title list(
x = var_inputs()$x,
y = var_inputs()$y,
z = var_inputs()$z,
alpha = var_inputs()$alpha,
size = var_inputs()$size,
plot_title = plot_title
) })
- 1
-
Variable inputs (from
selected_vars
)
- 2
-
inputs()
forscatter_plot()
Now that we’ve confirmed var_inputs()
has been created, we’ll verify the values are passed correctly from var_inputs()
to inputs()
(which is used to create the scatter plot).
To do this, we’ll progress through the module function (using n
in the debugger console or by clicking Next) until the inputs()
reactive has been created,
n
in the debugger or click Next to progress through the function
2]> inputs() Browse[
$x
[1] "imdb_rating"
$y
[1] "audience_score"
$z
[1] "mpaa_rating"
$alpha
[1] 0.5
$size
[1] 2
$plot_title
[1] ""
These two steps have shown us 1) the modules are communicating properly, and 2) the scatter plot display module contains the list of reactive values needed to render the graph.
Create abstract syntax trees with the
ast()
function from thelobstr
package.↩︎Watch this video to learn about call stacks and abstract folder trees with
lobstr
.↩︎Stack traces are also covered in Advanced R, 2ed, Mastering Shiny, and in the Shiny documentation. I’ve summarized some tips on reading Shiny call stacks in the stack traces section on the Appendix.↩︎