How to Write Your First tryCatch() Function in R

Learn essential best practices for writing your first tryCatch() function in R. Master error handling, syntax fundamentals, and practical examples to create more robust R code. Perfect for R programmers.
code
rtip
Author

Steven P. Sanderson II, MPH

Published

April 28, 2025

Keywords

Programming, tryCatch function R, error handling R, R programming, R exception handling, R code robustness, R tryCatch examples, handling warnings R, R programming best practices, debugging in R, R function syntax, how to use tryCatch in R, error handling techniques in R programming, best practices for tryCatch function in R, R programming for error management, robust coding using tryCatch in R

Key Takeaways:

  • The tryCatch() function helps handle errors and warnings in R code
  • It consists of expression, error handler, warning handler, and finally blocks
  • Using tryCatch() makes your code more robust and prevents crashes
  • It’s particularly useful for file operations, database connections, and complex calculations

Introduction

The tryCatch() function in R is a powerful tool that helps you handle errors and warnings that might occur during code execution. By using tryCatch(), you can ensure your program continues running even when unexpected issues arise. This tutorial will guide you through writing your first tryCatch() function with clear explanations and working examples.

Understanding the Syntax

The basic syntax of the tryCatch() function is straightforward:

tryCatch(
  expr,                           # The expression to evaluate
  error = function(e) { ... },    # Code to handle errors
  warning = function(w) { ... },  # Code to handle warnings
  finally = { ... }               # Code that always executes
)

Let’s break down each component:

  1. expr: The R code you want to run and monitor for errors or warnings
  2. error: A function that runs if an error occurs in your expression
  3. warning: A function that runs if a warning occurs in your expression
  4. finally: Code that executes regardless of whether errors or warnings occurred

Basic Working Examples

Example 1: Handling Errors

Let’s start with a common error - attempting to take the square root of a non-numeric value:

result <- tryCatch({
  # This will cause an error
  sqrt("a")
}, error = function(e) {
  cat("An error occurred:", conditionMessage(e), "\n")
  NA  # Return NA instead of crashing
})
An error occurred: non-numeric argument to mathematical function 
print(result)
[1] NA

Explanation: When we try to take the square root of “a”, R generates an error. Our error handler catches this, prints a message, and returns NA instead of letting the program crash.

Example 2: Handling Warnings

Now let’s handle a warning that occurs when calculating the logarithm of a negative number:

result <- tryCatch({
  # This will cause a warning
  log(-1)
}, warning = function(w) {
  cat("A warning occurred:", conditionMessage(w), "\n")
  NaN  # Return NaN as the result
})
A warning occurred: NaNs produced 
print(result)
[1] NaN

Explanation: Taking the logarithm of a negative number produces a warning in R. Our warning handler catches this, displays a message, and returns NaN.

Example 3: Using the Finally Block

The finally block is useful for cleanup operations that should always run:

result <- tryCatch({
  sqrt("a")  # This will cause an error
}, error = function(e) {
  cat("An error occurred:", conditionMessage(e), "\n")
  NA
}, finally = {
  cat("This block always executes, regardless of errors or warnings.\n")
})
An error occurred: non-numeric argument to mathematical function 
This block always executes, regardless of errors or warnings.
print(result)
[1] NA

Explanation: The finally block runs after everything else, whether or not an error occurred. This makes it perfect for cleanup operations like closing file connections or database connections.


Comparison: tryCatch() vs try()

R offers two main error handling approaches: tryCatch() and the simpler try(). Here’s how they compare:

Feature tryCatch() try()
Basic Syntax tryCatch(expr, error=function(e){…}) try(expr)
Error Handling Dedicated handler function Basic error catching
Warning Handling Separate warning handler No direct warning handling
Return Value Control Full control via handlers Returns error object
Multiple Condition Types Yes - multiple handlers No - only errors
Cleanup Operations Uses finally block No cleanup block

Common Error Types and How to Handle Them

Error Type Description Handling Method
simpleError Basic error type for most R errors tryCatch(error = function(e) {…})
warning Warning messages that don’t stop execution tryCatch(warning = function(w) {…})
try-error Result of a failed try() attempt if(inherits(result, “try-error”)) {…}
condition Base class for all conditions tryCatch(condition = function(c) {…})
custom error User-defined error class tryCatch(customError = function(e) {…})

Practical Use Cases

Example 4: Handling Errors in a Loop

Sometimes you need to process multiple items and want to continue even if some fail:

results <- c()
for (i in 1:6) {
  result <- tryCatch({
    # Generate an error for the third iteration
    if (i == 3) stop("Error at iteration 3!")
    i^2  # Square the number
  }, error = function(e) {
    cat("An error occurred:", conditionMessage(e), "\n")
    NA  # Return NA when there's an error
  })
  
  results <- c(results, result)
}
An error occurred: Error at iteration 3! 
print(results)
[1]  1  4 NA 16 25 36

Explanation: We’re processing numbers 1 through 6, but deliberately causing an error when i equals 3. The error handler allows the loop to continue, returning NA for the failed calculation.

Example 5: Processing Lists with Mixed Data Types

Let’s handle a list that contains both numbers and non-numeric values:

nums <- list(12, 88, 39, "Ten", 51, 12)

# Function to divide by 5
div_by_5 <- function(n) {
  return(n / 5)
}

# Apply the function to each element, handling errors
divided_out <- sapply(nums, function(x) {
  tryCatch({
    div_by_5(x)
  }, error = function(e) {
    return(NA)  # Return NA for non-numeric values
  })
})

print(divided_out)
[1]  2.4 17.6  7.8   NA 10.2  2.4

Explanation: We’re trying to divide each element by 5, but “Ten” causes an error. tryCatch() lets us handle this gracefully and continue processing the rest of the list.

Example 6: File Operations with Error Handling

Reading files that might not exist is a common use case for error handling:

read_file_safely <- function(filepath) {
  tryCatch({
    # Try to read the file
    data <- read.csv(filepath)
    return(data)
  }, error = function(e) {
    # Handle the error if file doesn't exist or has issues
    message("Could not read file: ", filepath)
    message("Error: ", conditionMessage(e))
    return(NULL)
  }, warning = function(w) {
    # Handle warnings (like parsing issues)
    message("Warning while reading file: ", conditionMessage(w))
    # Continue with the result despite the warning
  })
}

# Example usage:
data <- read_file_safely("nonexistent_file.csv")
Warning while reading file: cannot open file 'nonexistent_file.csv': No such file or directory
# This won't crash your program

Explanation: This function tries to read a CSV file but handles errors gracefully if the file doesn’t exist or has other issues.


Best Practices in Error Handling

Practice Recommendation Example
Error Message Clarity Use descriptive error messages stop(“Invalid input: value must be numeric”)
Warning vs Error Warnings for recoverable issues, errors for fatal problems if(x < 0) warning(“Negative value”) else stop(“Fatal error”)
Cleanup Code Always include finally block for cleanup operations tryCatch(expr, finally={close(conn)})
Return Values Return meaningful values from error handlers tryCatch(expr, error=function(e) NA)
Error Logging Log errors appropriately for debugging tryCatch(expr, error=function(e) log_error(e))

Advanced Example: Database Connection

Database operations are perfect candidates for error handling since many things can go wrong:

connect_to_db <- function(db_name, user, password) {
  tryCatch({
    # This assumes the DBI package is loaded
    # In a real scenario, load required libraries first
    conn <- dbConnect(RSQLite::SQLite(), dbname = db_name)
    message("Successfully connected to database")
    return(conn)
  }, error = function(e) {
    message("Failed to connect to database: ", conditionMessage(e))
    return(NULL)
  }, warning = function(w) {
    message("Warning during connection: ", conditionMessage(w))
    # Return the connection despite the warning
  }, finally = {
    message("Connection attempt completed")
  })
}

Explanation: This function tries to connect to a database and returns the connection if successful. If an error occurs, it returns NULL instead of crashing.

Your Turn!

Now that you understand the basics of tryCatch(), try writing a function that:

  1. Takes a vector of file paths
  2. Tries to read each file
  3. Returns the contents of files that exist, and NA for those that don’t
See Solution
read_multiple_files <- function(file_paths) {
  results <- list()
  
  for (i in seq_along(file_paths)) {
    results[[i]] <- tryCatch({
      readLines(file_paths[i])
    }, error = function(e) {
      message("Could not read file: ", file_paths[i])
      NA
    })
  }
  
  names(results) <- file_paths
  return(results)
}

# Example usage:
files <- c("existing_file.txt", "nonexistent_file.txt")
contents <- read_multiple_files(files)
Warning in readLines(file_paths[i]): incomplete final line found on
'existing_file.txt'
Warning in file(con, "r"): cannot open file 'nonexistent_file.txt': No such
file or directory
Could not read file: nonexistent_file.txt

Conclusion

The tryCatch() function is an essential tool for writing robust R code that can handle unexpected situations gracefully. By properly implementing error handling, you can create more reliable programs that provide meaningful feedback when things go wrong rather than simply crashing.

Start small by adding tryCatch() to operations that commonly fail, like file reading or web API calls. As you become more comfortable with the pattern, you can implement more sophisticated error handling strategies throughout your code.

Remember that good error handling isn’t just about preventing crashes—it’s about creating a better experience for users of your code, including your future self!

FAQ

1. When should I use tryCatch() versus try()?

Use tryCatch() when you need fine-grained control over different types of conditions (errors vs. warnings) or when you need to perform cleanup operations. Use try() for simpler cases when you just want to prevent a function from stopping execution.

2. Can I create my own custom error types in R?

Yes, you can create custom error classes by extending the condition system. This is useful for distinguishing between different types of errors your code might encounter.

3. Does using tryCatch() make my code slower?

The overhead of tryCatch() is negligible in most cases. The benefits of preventing crashes and handling errors properly far outweigh any minor performance impact.

4. How do I access the original error message in an error handler?

Use conditionMessage(e) where e is the error object passed to your handler function.

5. Can tryCatch() handle multiple types of errors differently?

Yes, you can use inheritance to catch different error types. For example, you can handle “simpleError” differently from “customError” by checking the class of the error object.

References

Official Documentation

  1. R Documentation. “trycatch: Evaluates an expression with the possibility to catch exceptions”
    https://www.rdocumentation.org/packages/R.oo/versions/1.2.7/topics/trycatch

  2. CRAN. “Error handling in R with tryCatchLog: Catching, logging, post-mortem analysis”
    https://cran.r-project.org/web/packages/tryCatchLog/vignettes/tryCatchLog-intro.html

  3. RStudio. “6 The R API: entry points for C code”
    https://rstudio.github.io/r-manuals/r-exts/The-R-API.html

Books and Academic Resources

  1. Advanced R by Hadley Wickham. “8 Conditions”
    https://adv-r.hadley.nz/conditions.html

  2. Mastering Software Development in R. “Error Handling and Generation”
    https://bookdown.org/rdpeng/RProgDA/error-handling-and-generation.html

Technical Blogs and Tutorials

  1. Medium. “Catch Me If You Can: Exception Handling in R”
    https://medium.com/number-around-us/catch-me-if-you-can-exception-handling-in-r-2e0f6c473a28

  2. GeeksforGeeks. “Handling Errors in R Programming”
    https://www.geeksforgeeks.org/handling-errors-in-r-programming/

  3. FavTutor. “tryCatch() function in R”
    https://favtutor.com/blogs/trycatch-function-in-r

  4. Advanced R by Hadley Wickham. “Debugging, condition handling, and defensive programming”
    http://adv-r.had.co.nz/Exceptions-Debugging.html

Package Documentation

  1. RDrr.io. “tryCatchLog documentation”
    https://rdrr.io/cran/tryCatchLog/man/

Have you used tryCatch() in your R projects? Share your experiences in the comments below!


Happy Coding! 🚀

tryCath() in R

You can connect with me at any one of the below:

Telegram Channel here: https://t.me/steveondata

LinkedIn Network here: https://www.linkedin.com/in/spsanderson/

Mastadon Social here: https://mstdn.social/@stevensanderson

RStats Network here: https://rstats.me/@spsanderson

GitHub Network here: https://github.com/spsanderson

Bluesky Network here: https://bsky.app/profile/spsanderson.com

My Book: Extending Excel with Python and R here: https://packt.link/oTyZJ

You.com Referral Link: https://you.com/join/EHSLDTL6