Skip to content

R pkg: argument to control how errors are displayed in the chat #276

Description

@xx02al

When an error occurs while streaming an assistant response (e.g. inside chat$stream_async()), chat_append_stream() catches it and appends (via chat_append_message()) sanitized_chat_error(reason) into the chat UI as a normal chat bubble.

sanitized_chat_error() lives in pkg-r/R/utils.R, along with further helpers:

needs_sanitized <- function(err) {
  isTRUE(getOption("shiny.sanitize.errors")) &&
    !inherits(err, "shiny.custom.error")
}

sanitized_error_message <- function(err) {
  if (needs_sanitized(err)) {
    "An error occurred. Please try again or contact the app author."
  } else {
    strip_ansi(conditionMessage(err))
  }
}

notify_error <- function(prefix, err) {
  shiny::showNotification(
    paste0(prefix, ": ", sanitized_error_message(err)),
    type = "error",
    duration = NULL
  )
  rlang::warn(prefix, parent = err)
}

sanitized_chat_error <- function(err) {
  if (needs_sanitized(err)) {
    sprintf("\n\n**%s**", sanitized_error_message(err))
  } else {
    sprintf(
      "\n\n**An error occurred:**\n\n```\n%s\n```",
      sanitized_error_message(err)
    )
  }
}

As we see, if shiny.sanitize.errors is explicitly set to TRUE, the chat shows the generic message. Otherwise, the chat bubble will show: **An error occurred:** followed by the raw conditionMessage(). A conditionMessage() text is whatever it is—it may be satisfactory, but chances are that it is not helpful, reveals confidential information, ...

shiny.sanitize.errors is a global option. If an app developer wants to scope it to a single shinychat function, or to otherwise take ownership of a specific error message shown by shinychat, things get complicated. Not because of a flaw in the implementation—shiny.sanitize.errors being read asynchronously, inside chat_append_stream()'s own promises::catch() handler, is absolutely fine as is.

Proposal

Add an argument to the following functions, that allows app authors to define how the error is displayed in the chat:

I haven't thought about the name of the argument yet, and I'm open to what values the argument should take. Here are some initial notes:

  • Argument name error_message, ... . on_error would likely be misleading in multiple ways.
  • Accepted values String? Function? Enum, mirroring Python: "auto" ("actual" or "sanitize" depending on shiny.sanitize.errors)/"actual" (e.g. what we now have if shiny.sanitize.errors is not TRUE) / "sanitize" (e.g. what we now have if shiny.sanitize.errors is TRUE) ("unhandled" may or may not make sense)? Or a combination of some of them?

Python package

shinychat's Python Chat class exposes an on_error parameter, set once per Chat instance. However, Python's enum options control whether/how a popup appears, while the R proposal is about what text appears in the chat itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: MediumValid bug or well-defined request with moderate impact or a workaround.ai-triage:doneMarks an issue whose AI triage workflow is complete.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions