The contents for this section are under development. Thank you for your patience.
In the previous chapters we demonstrated how to combine browser() and observe() for interactive debugging. We also covered how to combine reactiveValuesToList(), verbatimTextOutput(), and renderPrint() to view reactive values in the UI.
When debugging Shiny apps, it’s likely you’ll use a combination of these methods (depending on the bug or issue).
Access the applications in this chapter
I’ve created the shinypak R package in an effort to make each section accessible and easy to follow. Install shinypak using pak (or remotes):
Shiny app-packages can quickly become a complex and intertwined web of functions: utility functions, modules, user interface (UI) elements, server logic, etc. Before we jump into debugging modules, we’ll revisit their function and purpose by considering the following description of namespaces from the NS() function documentation:
“a namespace is to an ID as a directory is to a file.”
To visualize the hierarchical relationship between the functions in our app, we’ll use abstract syntax trees (ast()) from the lobstr package.1
For example, launch_app() calls the display_type() utility function, then movies_ui() and movies_server():
Abstract folder trees can be used to help construct a simplified call stack for applications (especially if we are using multiple utility functions or nested modules).
12.1 Debugging modules
In this branch of sap, the inputs has been split into two separate modules: variable inputs and aesthetic inputs.
We see the following in the sidebar after loading, documenting, installing and launching our app;
Ctrl/Cmd + Shift + L / D / B
Reactive values for the variable input module:
Reactive values for the aesthetic input module:
Reactive values in aesthetics input module
In the variable and aesthetic module namespaces, NS() ensures all the inputs are unique within the module (even when multiple modules are used in the same application).
You probably noticed that I’ve renamed the inputId for the plot title to x, but we avoid any namespace collisions because each ID is stored safely within a module.
<list>├─alpha: 0.5├─size: 2└─x:""
This encapsulation is similar to how a directory provides a distinct context for files, preventing naming conflicts within a file system.
The printed output from movies_ui() andmovies_server() is prefixed with the namespace (i.e., vars- or aes-), reflecting the hierarchical organization of the input IDs, much like file paths in a directory structure.
Reactive values in movies_ui()
By using reactiveValuesToList() and lobstr::tree() in combination with verbatimTextOutput() and renderPrint(), we are effectively debugging and inspecting the module’s reactive inputs.
Capturing and rendering reactive values in the UI gives us the benefits of print debugging while our Shiny app is running. When it’s combined with observe() and browser(), we can get a direct view of the application state and the program flow at specific execution points.
When we load, document, install and view the application in this branch we find an error with the graph display:
Error in graph display
The call stack (printed to the Console) displays the following information:
Warning: Error in tools::toTitleCase: 'text' must be a character vector208: stop207: tools::toTitleCase206:<reactive>204: .func201: contextFunc200: env$runWith189: ctx$run188: self$.updateValue186: inputs178: renderPlot176: func136: drawPlot122:<reactive:plotObj>102: drawReactive89: renderFunc88: output$plot-scatterplot3: runApp2: print.shiny.appobj1:<Anonymous>
The output in the Console is helpful (we know the error is coming from the tools::toTitleCase() function), but we need to narrow it down because we’re using this function in multiple places.
To use the interactive debugger, we’ll add browser() and observe() in the movies_server() function to capture the behaviors of both the variables and aesthetics modules:
Then we’ll load, document, install and launch the app:
Ctrl/Cmd + Shift + L / D / B
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.
We’ll proceed with the interactive debugger until both selected_vars and selected_aes are returned from the input modules:
Browse[1]> nBrowse[1]> nBrowse[1]> n
Reactive inputs returned from modules
This tells us both objects are returned from their respective module server functions. But we should pause and examine the structure of selected_aes:
The output above display selected_vars the method, not the selected input values. To view these, we need to include the parentheses:
Browse[1]> str(selected_aes())List of 3 $ alpha : num 0.5 $ size : int 2 $ plot_title: chr ""
This confirms that the UI values are being collected by the aesthetic input module and stored in selected_aes, so the error must be coming from inside one of the modules.
We’ve inspected the values in the exchange between movies_ui() and movies_server():
█─launch_app
├─█─movies_ui
└─█─movies_server
We’ll remove (or comment out) the calls to observe() and browser() from movies_server() and repeat a similar process in mod_scatter_display_server(), but include the calls to observe() and browser() after moduleServer(). Then we’ll load, document, and install the package and run the application again:
Wrap browser() in observe() and place after the call to moduleServer()
Ctrl/Cmd + Shift + L / D / B
Advance the debugger to the line afterinputs() is created with var_inputs() and aes_inputs()
We can see the input()$plot_title appears to be a character, but the structure of inputs() returns the error we saw in the Console
12.2.1 Verifying inputs
Now that we’ve isolated the error to the tools::toTitleCase() call in the creation of the inputs() reactive. We can confirm this in the interactive debugger:
Browse[1]> tools::toTitleCase(aes_inputs()$x)Error in tools::toTitleCase(aes_inputs()$x) : 'text' must be a character vector
Upon further inspection, we discover the source of the error:
Bug
Fix
tools::toTitleCase(aes_inputs()$x)
tools::toTitleCase(aes_inputs()$plot_title)
The approach above have shown us 1) all modules are communicating properly, and 2) where to make changes in the scatter plot display module to render the graph.
Module communication
I’ve reverted the code to display the error in this branch so it’s easier to follow along. Each of the apps in the inst/ folder have also been updated, and I encourage you to explore how the bug effects launching each app structure.
My approach to debugging
The Shiny documentation also has a list of methods for debugging apps, and learning how to read call stacks (or a stacktrace) will help you debug your shiny app.4, 5