Technical Articles
Handling errors and timeouts in a scenario
1. Introduction
My name is Samir Hamichi, I’m part of the SAP Intelligent Robotic Process Automation Customer Adoption Team.
In a series of blog posts, I want to give some best practices from the development perspective. I will focus on the Low Code/No code approach.
This blog post will cover errors and timeouts handling. First, I’ll explain the content of the default generated handlers: sc.onTimeout() and sc.onError(). In addition to the data you access via their parameters, I’ll give you some insights about useful global data you can leverage in these callbacks like information related to the user or computer running the project, information about the project like version, title, etc…
From the workflow designer, I’ll show the Error and Timeout activities and how to use them connecting additional logging activities before ending the scenario. I added some useful function that logs errors into Business Activity Monitoring system, CSV file or database.
Last, I show how to handle errors and timeouts within a given step. This gives you the possibility to do custom handling.
This Blog post is related to a webinar: How to manage errors during bot execution.
2. Context and steps
I’ll show you a very simple scenario with a custom step to explain the generated code and then add some custom code that allow me to illustrate handling of some errors and timeouts.
2.1. Workflow creation
I create a project named ErrorTimeoutMgt and create a workflow called FundsProcessing with one custom step. I save and build the project to generate the scenario script.
2.2. Scenario declaration
From the generated code, we extract the declaration of the scenario as a bloc:
// ----------------------------------------------------------------
// Scenario: FundsProcessing
// ----------------------------------------------------------------
GLOBAL.scenario({ FundsProcessing: function(ev, sc) {
var rootData = sc.data;
sc.setMode(e.scenario.mode.clearIfRunning);
sc.setScenarioTimeout(600000); // Default timeout for global scenario.
sc.onError(function(sc, st, ex) { sc.endScenario(); }); // Default error handler.
sc.onTimeout(30000, function(sc, st) { sc.endScenario(); }); // Default timeout handler for each step.
sc.step(GLOBAL.steps.Custom);
}}, ctx.dataManagers.rootData).setId('5c004f3a-7b59-45c3-972e-2cf7a7e1f207') ;
Among the different components of the scenario, we distinguish the following:
- A mode as starting condition according to business rules: here in our example, clear if there is a running instance of the scenario.
sc.setMode(e.scenario.mode.clearIfRunning);
You can access the different modes with code completion.
- A timeout for the whole scenario: default timeout for the whole scenario in milliseconds.
sc.setScenarioTimeout(600000); // Default timeout for global scenario.
- A state machine of the steps used in the scenario: this scenario is very simple with only one step.
sc.step(GLOBAL.steps.Custom);
Now, let’s focus on the Error and Timeout callbacks. In the snippet, we extract the code of the sc.onError & sc.onTimeout callbacks and add some indentation, we notice that it contains only an end scenario instruction: sc.endScenario.
- A callback for errors/exceptions handling: default error handler raised whenever an exception occurs during execution of the scenario.
sc.onError(function(sc, st, ex) {
sc.endScenario();
}); // Default error handler.
- A callback for timeout handling with timeout value: each step has a timeout, usually set to 30000 milliseconds. If the execution of the step exceeds this defined timeout, the callback is raised.
sc.onTimeout(30000, function(sc, st) {
sc.endScenario();
}); // Default timeout handler
Indeed, whenever an error occurs during runtime, as default behavior, the scenario ends without additional.
Notice that each callback has its own input parameters. they allow us to add some additional code like logging/cleaning data, resetting of processes/application states, etc.
Both of the callbacks have access to the current running scenario (sc) and the current running step (st).These objects have many parameters and functions that we can use inside the callbacks.
- the current running scenario (sc)
- the current running step (st)
The only difference between the two callbacks is the exception parameter (ex) that is specific for the sc.onError callback. It contains the details of the raised exception:
- the current raised exception (ex)
Inside the scope of the callbacks, you can have access to additional information. One of the useful modules is the ctx.options module. It contains the project management options (Documentation).
Here is my selection of those I usually use:
- the module ctx.options:
- Computer name: ctx.options.computerName
- User login: ctx.options.userName
- Project name: ctx.options.projectName
- Project client: ctx.options.projectClient
- Project comment: ctx.options.projectComment
- Project title: ctx.options.projectLabel
- Project uid: ctx.options.projectUid
- Project version: ctx.options.projectVersion
- Trace:
- Auto-recording: ctx.options.trace.autoRecording
- Different path variables:
- Folder containing Desktop Agent binaries: ctx.options.path.exec
- Folder containing project files: ctx.options.path.local
- Folder log and work files: ctx.options.path.log
- Folder containing server project files: ctx.options.path.server
- Folder containing resources files: ctx.options.path.resources
We will comeback to use them later in the implementation of 3 functions as additional log configurations .
For now, let’s go back to the workflow designer and show you two very important activities related to our callbacks. Error activity & Timeout activity.
2.3. Scenario Error and Timeout activities
Among the scenario activities we distinguish Error and Timeout activities illustrated below:
When used in the workflow, the Error activity (resp. Timeout activity) override the onError callback (resp. onTimeout callback) of the scenario.
The configuration below shows how we can use the Error activity. Respectively, the same could be done for the Timeout activity.
- First, drag and drop the Error activity into the workflow chart
- Second, add a log activity with the following properties:
- Message: “An exception has been raised, error:”+ex.name +” – message:’+ex.message
- Level: Error
- Note that here we can:
- add others information concatenated.
- call a global function in the Message property.
- Third, add an explicit End scenario activity
Below, the corresponding generated code of the previous configuration:
// ----------------------------------------------------------------
// Scenario: FundsProcessing
// ----------------------------------------------------------------
GLOBAL.scenario({ FundsProcessing: function(ev, sc) {
var rootData = sc.data;
sc.setMode(e.scenario.mode.clearIfRunning);
sc.setScenarioTimeout(600000); // Default timeout for global scenario.
// Error management
sc.onError(function(sc, st, ex) {
ctx.workflow('FundsProcessing', 'ee228015-5aa8-4390-8494-f5ef1178a860') ;
// Write log
ctx.log('An exception has been raised!'+ex.message, e.logIconType.Info);
ctx.workflow('FundsProcessing', '2afd7a3b-3673-4639-93a5-3cf601a099a1') ;
// End scenario
sc.endScenario();
return;
}); // Error handler.
sc.onTimeout(30000000, function(sc, st) { sc.endScenario(); }); // Default timeout handler for each step.
sc.step(GLOBAL.steps.Custom);
}}, ctx.dataManagers.rootData).setId('5c004f3a-7b59-45c3-972e-2cf7a7e1f207') ;
In the second step (Log activity), we can think of other activities like
- adding logs in the Business Activity Monitoring system
- writing in a file: excel, csv, json, text, etc.
- calling an external global function that writes into a database
2.4. Additional log configurations
In the following examples, I’ve created in a script Settings.js of my project. It contains the 3 following functions:
- logBAMfailure(sc, st)
- logCSVfailure(sc, st)
- logDBfailure(sc, st)
I use these external functions in my log activities to save my errors in different locations. They are generic function that can be reused in several scenarios and projects.
2.4.1. Configuration A: Business Activity Monitoring (BAM)
The Message property is a call for a function logBAMfailure(sc, st)
sc.onError(function(sc, st, ex) {
// Writing logs in Business Activity Monitoring
ctx.log(logBAMfailure(sc, st), e.logIconType.Error);
ctx.workflow('FundsProcessing', '2afd7a3b-3673-4639-93a5-3cf601a099a1') ;
// End scenario
sc.endScenario();
return;
}); // Error handler.
The implementation of logBAMfailure(sc, st) function:
// ----------------------------------------------------------------
// Logs in BAM
// ----------------------------------------------------------------
var logBAMfailure = function(sc, st){
try {
ctx.monitor.notifyError("Alert1", sc.code, "Exception raised", "Scenario failed in step"+st.name+ " - sc.label:"+sc.label);
} catch (error) {
ctx.log('BAM insertion failed: '+ error.description );
}
}
If you want to get more on BAM activities:
- see this interesting Blog post: Monitoring Business Activity in your Bots
- see the official documentation
2.4.2. Configuration B: Write in CSV
The Message property is a call for a function logCSVfailure(sc, st)
sc.onError(function(sc, st, ex) {
// Writing logs in CSV
ctx.log(logCSVfailure(sc, st), e.logIconType.Error);
ctx.workflow('FundsProcessing', '2afd7a3b-3673-4639-93a5-3cf601a099a1') ;
// End scenario
sc.endScenario();
return;
}); // Error handler.
The implementation of logCSVfailure(sc, st) function:
// ----------------------------------------------------------------
// Logs in CSV
// ----------------------------------------------------------------
var logCSVfailure = function(sc, st){
/*
Strings for CSV entry:
- Timestamp
- Computer name
- Username
- Project name
- Project client
- Project comment
- Project title
- Project Uid
- Project version
- Scenario name
- Step name
- Scenario code
- Scenario label
*/
try{
var entry = ctx.getTimestamp()+","+ ctx.options.computerName+","+ctx.options.userName+","+ctx.options.projectName+","+ctx.options.projectClient+","+ctx.options.projectComment+","+ctx.options.projectLabel+","+ctx.options.projectUid+","+ ctx.options.projectVersion+","+ sc.name+","+ st.name+","+ sc.code+","+ sc.label+"\r\n";
ctx.writeFile("c:\\temp\\ErrorsLogs_Comma_separator.csv", entry,true);
} catch (error) {
ctx.log('CSV log failed: '+ error.description );
}
}
2.4.3. Configuration C: Write in Database
The Message property is a call for a function logDBfailure(sc, st)
sc.onError(function(sc, st, ex) {
// Writing logs in Database
ctx.log(logDBfailure(sc, st), e.logIconType.Error);
ctx.workflow('FundsProcessing', '2afd7a3b-3673-4639-93a5-3cf601a099a1') ;
// End scenario
sc.endScenario();
return;
}); // Error handler.
The implementation of logDBfailure(sc, st) function:
// ----------------------------------------------------------------
// Logs in Database
// ----------------------------------------------------------------
var logDBfailure = function(sc, st){
// Database connection
var string_connection = "Provider=SQLOLEDB;Server=yourServername\\SQLEXPRESS;Database=ErrorsMgtDB;UID=yourUser;PWD=yourPassword;";
var oConnection;
var rs;
try {
oConnection = new ActiveXObject("ADODB.Connection");
rs = new ActiveXObject('ADODB.Recordset');
oConnection.Open(string_connection);
ctx.log("Connecting to database...", e.logIconType.Event);
} catch (error) {
ctx.log('Connection failed: '+ error.description );
}
/*
select the information you need for the database and construct your SQL insert statement skipping the cote /'/ charachter if any (Done for sc.label in this case)
I use the following fields:
- Timestamp
- Computer name
- Username
- Project name
- Project client
- Project comment
- Project title
- Project Uid
- Project version
- Scenario name
- Step name
- Scenario failure code
- Scenario failure label
*/
var insert_request = "INSERT INTO [ErrorsMgtDB].[dbo].[ErrorsTable] ([UID],[computerName],[userName],[projectName],[projectClient],[projectComment],[projectLabel],[projectUid],[projectVersion],[scenarioName],[stepName],[failureCode],[failureLabel]) VALUES ('"+ctx.getTimestamp()+"','"+ ctx.options.computerName+"','"+ctx.options.userName+"','"+ctx.options.projectName+"','"+ctx.options.projectClient+"','"+ctx.options.projectComment+"','"+ctx.options.projectLabel+"','"+ctx.options.projectUid+"','"+ ctx.options.projectVersion+"','"+ sc.name+"','"+ st.name+"','"+ sc.code+"','"+ sc.label.replace(/'/g," " )+"')";
try {
// Insert querry
ctx.log("Insert logs to database...", e.logIconType.Event);
rs = new ActiveXObject('ADODB.Recordset');
rs.Open(insert_request, oConnection);
ctx.log("Data logged with success!", e.logIconType.Event);
} catch (error) {
ctx.log('Insertion failed: '+ error.description );
}
}
2.5. Handling error and timeout within a step
Right now, I have shown how to handle errors at the level of the scenario (respectively, the same applies to the timeout callback). Now, what if we want to handle the error and timeout within the step?
We have to customize the implementation of the step to add an st.onError to handle errors. Whenever a step is executed, the scenario starts a timer that has a timeout value from the default definition. If you want to customize the timeout for the step, you define an st.onTimeout callback with a custom timeout value. in the code below, it’s set to 60 seconds instead of the default which is set to 30 seconds.
// ----------------------------------------------------------------
// Step: Custom
// ----------------------------------------------------------------
GLOBAL.step({ Custom: function(ev, sc, st) {
var rootData = sc.data;
ctx.workflow('FundsProcessing', 'c4d5018b-67cc-47b5-a135-c9a62df79773') ;
// Custom
st.onError( function(sc, st, ex) {
// My custom code if error raised in this step
sc.endScenario();
});
st.onTimeout( 60000, function(sc, st) {
// My custom code if step timeout reached in this step
sc.endScenario();
});
//Step code here
sc.endStep(); // end Scenario
return;
}});
3. Conclusion
In this blog post, I presented how to handle errors and timeouts of a certain scenario. I explained the code of the default callbacks generated in the scenario. I gave some insight about the accessible data that could be interesting and useful for your logs and Business Activity Monitoring.
I presented the Error and Timeout activities that you can use in the workflow designer to override the default handlers and connect additional activities before ending the scenario.
I added some useful functions that log errors in different ways: Business Activity Monitoring system, CSV file and SQL database. These functions can be taken and customized to your projects.
Last I shown how to handle errors and timeouts within a given step. This gives you the possibility to do custom handling for each step.
As a last reminder, this Blog post is related to a webinar: How to manage errors during bot execution.
Much needed Blog!!Thanks Samir!!
Great stuff!
Excellent, thank you!
My 2 cents:
Error and Timeout might also depend on the load of the machines/network involved, in case some other tasks/users are active.
Thank you Samir. Really useful blog!
I would like to know if we define both the error handlers within some steps and at the scenario level, will the previous ones override the later one?
Thanks, useful information!!
I would like to disable the timeout for a specific SAP event. I have already modified the Timeout of the sequences and the workflow, but I would like to disable the Timeout of the events because I get the following error "Execution timeout in event: GLOBAL.:evReceiveMessage" every time I run a macro from SAP IRPA that lasts more than 5 minutes running, at the end of the run macro and resume workflow activities it always fails.
Thanks in advance.
Regards, Marcela
Hello! thank you for share yours knowledges. This this has clarified some doubts.
But I have two questions:
thank you again!
Thank you Samir.
I would like to find out what is the best practices when indicating the timeout value.
I have this scenario:
One transaction runs for 5 mins.
I may have one transaction or hundreds of transaction when the process is run and I am not able to control the number of transactions to run each time.
What should I set the timeout to be?
Thank you for your help!