Exception in Shiny apps can crash the app. Most time we don't want the app to crash but just stop this code block, inform users and continue with other code blocks. This function is designed to handle these issues.

shinyCatch(
  expr,
  position = "bottom-right",
  blocking_level = "none",
  shiny = TRUE,
  prefix = "SPS",
  trace_back = spsOption("traceback")
)

Arguments

expr

expression

position

client side message bar position, one of: c("top-right", "top-center", "top-left","top-full-width", "bottom-right", "bottom-center", "bottom-left","bottom-full-width").

blocking_level

what level you want to block the execution, one of "error", "warning", "message", default is "none", do not block following code execution.

shiny

bool, only show message on console log but not in Shiny app when it is FALSE. Useful if you want to keep the exception only to the server and hide from your users. You do not need to set it to FALSE when purely work outside shiny, it will automatically detect if you are working in a Shiny environment or not.

prefix

character, what prefix to display on console for the log, e.g. for error, the default will be displayed as "SPS-ERROR". You can make your own prefix, like prefix = "MY", then, it will be "MY-ERROR". Use "" if you do not want any prefix, like prefix = "", then, it will just be "ERROR". multiple levels

trace_back

bool, added since spsComps 0.2, if the expression is blocked or has errors, cat the full trace back? It will display called functions and code source file and line number if possible. Default follows the SPS spsOption("traceback") setting. You can set it by running spsOption("traceback", TRUE). If you do not set it, it will be FALSE. or you can just manually set it for each individual shinyCatch call shinyCatch({...}, trace_back = TRUE).

Value

see description and details

Details

Blocking

  • The blocking works similar to shiny's shiny::req() and shiny::validate(). If anything inside fails, it will block the rest of the code in your reactive expression domain.

  • It will show error, warning, message by a toastr bar on client end and also log the text on server console depending on the blocking_level (dual-end logging).

  • If blocks at error level, function will be stopped and other code in the same reactive context will be blocked.

  • If blocks at warning level, warning and error will be blocked.

  • message level blocks all 3 levels.

  • If blocking_level is other than these 3, no exceptions will be block, and if there is any error, NULL will return and following code will continue to run.

To use it

Since spsComps 0.3.1 to have the message displayed on shiny UI, you don't need to attach the dependencies manually by adding spsDepend("shinyCatch") or spsDepend("toastr") (old name) on UI. This becomes optional, only in the case that automatic attachment is not working.

Display

Messages will be displayed for 3 seconds, and 5s for warnings. Errors will never go away on UI unless users' mouse hover on the bar or manually click it.

environment

shinyCatch uses the same environment as where it is called, it means if you assign a variable inside the expression, you can still get it from outside the shinyCatch, see examples.

Examples

if(interactive()){
  ui <- fluidPage(
    spsDepend("shinyCatch"), # optional
    h4("Run this example on your own computer to better understand exception
           catch and dual-end logging", class = "text-center"),
    column(
      6,
      actionButton("btn1","error and blocking"),
      actionButton("btn2","error no blocking"),
      actionButton("btn3","warning but still returns value"),
      actionButton("btn4","warning but blocking returns"),
      actionButton("btn5","message"),
    ),
    column(
      6,
      verbatimTextOutput("text")
    )
  )
  server <- function(input, output, session) {
    fn_warning <- function() {
      warning("this is a warning!")
      return("warning returns")
    }
    observeEvent(input$btn1, {
      shinyCatch(stop("error with blocking"), blocking_level = "error")
      output$text <- renderPrint("You shouldn't see me")
    })
    observeEvent(input$btn2, {
      shinyCatch(stop("error without blocking"))
      output$text <- renderPrint("I am not blocked by error")
    })
    observeEvent(input$btn3, {
      return_value <- shinyCatch(fn_warning())
      output$text <- renderPrint("warning and blocked")
    })
    observeEvent(input$btn4, {
      return_value <- shinyCatch(fn_warning(), blocking_level = "warning")
      print(return_value)
      output$text <- renderPrint("other things")
    })
    observeEvent(input$btn5, {
      shinyCatch(message("some message"))
      output$text <- renderPrint("some message")
    })
  }
  shinyApp(ui, server)
}
# outside shiny examples
shinyCatch(message("this message"))
#> [SPS-INFO] 2023-10-07 07:25:50.292093 this message
#> 
try({shinyCatch(stop("this error")); "no block"}, silent = TRUE)
#> [SPS-ERROR] 2023-10-07 07:25:50.394241 this error
#> [1] "no block"
try({shinyCatch(stop("this error"), blocking_level = "error"); "blocked"}, silent = TRUE)
#> [SPS-ERROR] 2023-10-07 07:25:50.397951 this error
# get variable from outside
shinyCatch({my_val <- 123})
my_val
#> [1] 123