No matter how careful and precise we may be, errors are a normal part of any app. Some errors are unknown to us and statements such as Try/Catch allow us to handle them with ease. These statements help us effectively identify, catch and gracefully recover from runtime errors, making sure that our app continues to run smoothly and maintain a consistent user experience.
1. Understanding the Try-Catch Block
The purpose of a try block is to house risky workflows that could potentially throw an error during runtime. These runtime errors could be, but aren’t limited to, trying to access a non-existent function or an undefined variable.
The Catch block’s job is to be the safety net that will protect the app from crashing because of the error found inside our Try block. We can think of it as a backup plan in-case anything goes wrong while our users are working within our app.
Example
In the following image, we are simulating a situation where you might dynamically be fetching values and using them for calculations. In some cases, one of the values may be a 0 and would be used for a division workflow, which would end up with an error.
Try Block: Perform calculation
Catch Block: Return the error message
The Result
Inside Xano, when you select the Try/Catch function from our Utility Functions, you will see these 3 sections above and under the hood, we provide the following variables for you to use:
If we had chosen “name”, this would be our result:
If we had chosen “code”, this would be our result:
However, if we had chosen “result” in this case, there would be nothing returned however you would be able to customize this “result” using an update variable to contain information you will find necessary.
The great thing is that we can customize some of the other errors ourselves, which we will get into later into this article.
2. Exploring the Finally Block
The purpose of the Finally block is to execute whether an error was thrown or not. We excluded it in the previous example to illustrate that the statement may or may not have anything configured so the choice to use it is entirely yours.
Example
In the following example, we are simulating a workflow where we are using a Google Cloud Storage function as well as the External API Request function as a back up if we don’t get the result we are expecting due to reasons unknown to us.
In the first step, we define a variable that we will want to reference in multiple parts of our Try/Catch function.
If all goes well, our GCS function will replace the value, skip to the Finally block and it’s business as usual. If not, we are using a conditional to check if the result from the GCS function is empty (but we are sure the value exists) and if so, we use the Throw error function.
With this function, we can define our own custom error that will give us more context over the issue.
Going back to the variables provided to us in this Try/Catch function, If we choose “name”, this is the result:
If we choose “code”, this is the result:
If we choose “message”, this is the result:
If we choose “result”, this is the result, which you will notice is exactly what we defined in the Throw error step:
The remainder of the steps in our current workflow will be where we update our final_result variable to give us the result of the API call, along with the built-in result variable.
The final result being as follows:
Another way we can explore throwing errors, is through the use of the Pre-Condition function, which we will be diving into, in just a few.
Circling back to the current use case, other native feature pairings we could explore are using the Direct Database Query in the Try block and Query All Records in the Catch block or a Lambda Function and a long-winded Custom Function.
The goal here is to consider what backup configurations would match the configuration we have in our Try block so that our app still proceeds with the actions it’s meant to do. The thinking behind this is “If this is my Plan A, what can I do for Plan B that still gives the same outcome. Then Finally, tell me which one worked.”
3. Advanced Concept: Nested Try/Catch and Retrying
For more complex workflows, it may be necessary to nest these functions inside each other to enable more layered error handling. It may also be necessary to have your app log these events (Successful or Failed) inside a table for later reference or to have it rerun the workflow at a later stage if the workflows failed.
In this last example, we are simulating the signing of a Certificate of Completion whereby the Administrator User will submit their signature to append to the document that will be sent to their Student User upon completing a course.
In the event that it fails, we try to regenerate it with an already existing signature by another Administrator so that the end goal, which is to certify our Student, still goes through.
In our Try block, we are attempting the first API call with the dynamic signature.
Then we use our Pre-Condition to throw the error if our status is not 200
and lastly, we create an event variable that will be useful for our Event Log table that will tell us whether the first attempt succeeded or failed.
In our Catch block, we have another Try block that will first replace our most recent event variable then Add a Record to our Event Log table then run a similar function whose difference is it not accepting a dynamic signature. From there it is met with another Pre-Condition that will throw an error as well or proceed to replace our event with one similar to our first one but with 2 in brackets to indicate that this was a second attempt.
In the case that the error did get thrown, our inner Catch block’s job is to replace the event variable to show that we are now at another stage where the workflow has failed for the second time and therefore we would like to log this to our Scheduler table that will be linked to a task that will attempt to generate the PDF. In here, we will want to track the inputs provided so that we can reuse them without the need for manual intervention.
Our inner Finally block contains nothing but our outer Finally will log our second event that will either tell us whether the second attempt failed or not.
Going back to the variable provided for us in this Try/Catch function, If we choose “name” or “code”, we would get the same result as our first scenario where we do not have a customized error message. However, if we choose message or result, our result will be the values we defined inside our Pre-Condition, with message taking the Error Message input and Result taking the Payload input:
4. Best Practices in Using Try-Catch-Finally
It is important to be able to identify which scenarios absolutely need this function. Overusing it could lead to configurations that are hard to follow (for your collaborators) and maintain, while underusing them could leave your app vulnerable to errors that would cause a stain on your user experience.
Here are some tips to keep in mind:
Only use the Try/Catch function wherever there might be possibilities of errors.
Use the Catch block to log errors for debugging purposes and to create descriptive errors that will provide much needed context to your users and your teammates.
Avoid excessive performance overhead by using alternative functions for error handling in workflows that are less prone to errors. (Pre-conditions, If-Statements, etc)
Avoid being overly broad and be specific when customizing your error messages so that you are able to identify exactly which part of the workflow broke and what the exact cause was.
Additional Resources
Remember, error handling is an ongoing learning process. Here are some resources for further exploration:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
**********************************************************************************
Test Your Knowledge!
Bug Blast - Taming Errors with Try-Catch
The following program calculates total spending per customer from a data file. But wait! There are bugs lurking in the code, and your task is to identify them and use try-catch statements to handle these errors, ensuring the program runs smoothly.
Getting Started
Step 1. Install the following snippet inside your workspace https://www.xano.com/snippet/gF1HpzUw
Need help installing snippets? Check out our Preview and Add a Snippet Instructions
Step 2. Download the sample data with records for 21 unique clients https://x9qk-rkcz-tbuf.n7.xano.io/vault/k0U06HzM/4lDrtrogHAFRVdplEy7TxmcQOUo/DlaqZg../bad_data.rtf
Step 3. Paste the data from the file inside the Run and Debug
Step 4. When you run the API, the bug you will see is “Numbers required for mathematical operations”
Checklist (✅ Required 🔍 Optional)
✅ Final Result should include 21 items
✅ Identify where the error was coming from and explain how to prevent it
🔍 Throw custom error with payload
🔍 Create a event logger table to track each event inside the workflows
By completing this activity, you'll gain a practical understanding of how try-catch statements work and their importance in error handling. You'll also practice debugging techniques and improve your programming problem-solving skills.