Skip to main content
Version: 8.1

Error Handling

What is Error Handling

The concept of error handling is recognizing when an error might occur in a block of code, and instead of throwing the error, handling it gracefully. This can involve giving the user a more distinct error message, letting the user know that their attempt to run the code failed, or even undoing something that your code set it motion so that it can be started again from the same starting point.

Error Handling in Python, Java, and Jython

Within Python, we can use the try and except blocks to handle errors. We would first use try: and write the code we would like to try indented underneath it. We then must have an except: with code that will run if there is an error.

Pseudocode - Error Handling
# With try, we can attempt any amount of code
try:
some code
more code
even more code

# If any of lines in the try block were to throw an error, then we move down and run the block under except.
# The except statement is NOT optional: you must define what your code should do in the event an exception occurs.
except:
failover code

When running the code above, there is a specific order to the way it executes.

  1. The try block is run, and the code within is executed line by line.
    1. If an error occurs, the code in the try block will immediately stop and the except block will begin to execute.
    2. If no error occurs after all code in the try block has been executed, the try block will be finished and the code in the except block will be skipped.
  2. After either outcome, any code after the except will then execute.

Because of the way the try and except blocks work, it is very useful on situations that require user input, where something may have been incorrectly entered which would usually result in an error.

Much of the scripting in Ignition is done in Python; however, because Ignition is written in Java, many internal system calls may throw Java exceptions that Python won't catch without modifications.

Within Java, we can use the try and catch blocks to handle errors:

Pseudocode - Error Handling (Java)
// Use try to test our code
try {
testing code goes here
}

// Handle any errors the code throws
catch(Exception e) {
code to handle exceptions
}

However, since we are running Jython, we can use Python's try and except blocks for Java error handling by using Java's Exception class.

Pseudocode - Error Handling (Jython)
# Import the Exception class
import java.lang.Exception

# With try, we can attempt any amount of code
try:
code you want to test
more code
code that will throw an exception

# If any of lines in the try block were to throw an error, then we move down and run the block under except.
# The except statement is NOT optional: you must define what your code should do in the event an exception occurs.
except java.lang.Exception, e:
failover code

The Pass Keyword

The pass keyword is unique in that it does nothing except to fill in a spot where code is required. This is useful if we want to handle the exception so that it doesn't throw an error message, but we don't actually want to do anything with it. In this case, we can use pass to tell the script to continue.

Pseudocode - Pass Keyword
try:
some code
more code
even more code

except:
# The error will bring the code to the exception, and then the exception will simply do nothing.
pass

Error Handling Example

An easy way to demonstrate how error handling works is with a division example, since it is easy to cause an error by dividing by 0. Take the code below:

When we run it, we get a printed value of 50. There was no error in the division, and the try block finished successfully. However, if we were to change the value of x to 0, we can see that "An error occurred!" is printed.

Python - Error Handling Division
# We start with a value, which could represent some user input.
x = 2

# We use that value in our try block, and have a simple print statement if there is an error.
try:
value = 100/x
print value
except:
print "An error occurred!"

The following code block is also with a division example, but in Java:

Java - Error Handling Division
// We start with a value, which could represent some user input.
int x = 2;

// We use that value in our try block, and have a simple print statement if there is an error.
try {
int value = 100/x;
System.out.println(value);
}
catch (Exception e) {
System.out.println("An error occurred!");
}

Finally, the following code block is with the same division example, but in Jython:

Jython - Error Handling Division
# Import the Exception class
import java.lang.Exception

# We start with a value, which could represent some user input.
x = 2

# We use that value in our try block, and have a simple print statement if there is an error.
try:
value = 100/x
print value
except java.lang.Exception, e:
print "An error occurred!"

Exception-Specific Failover

While each try block must be followed by at least one except block, there can be multiple except blocks to handle different types of errors. This is done by listing the error object after the except keyword and before the colon. Looking back at the example above, I know that my most common error is going to be a divide by zero error. So I can make an exception that is specific to divide by zero errors.

Python - Exception-Specific Failover
# We start with a value, which could represent some user input.
x = 0

# Use the user input in division in our try block.
try:
value = 100/x
print value

# If the exception that would occur is of type ZeroDivisionError, we can run a specific block of code
except ZeroDivisionError:
print "Dividing by zero is not allowed. Please stop trying to divide by zero"
# We can then have a second exception without a specific error. This except acts as a catch-all;
# if the user caused an exception we didn't account for above, we can failover to the block of code below.
except:
print "An error occurred!"

The except blocks in the code above cover all possible errors as represented in the table below. Now, we have a more tailored approach to how we handle errors while still catching all of them that may occur.

Inputs (x value)Output
250
0Dividing by zero is not allowed. Please stop trying to divide by zero
'a'An error occurred!

Each try block can have many except blocks, and each except block can also name multiple types of errors as shown below. However, an error that happens within a try block will only ever trigger a single except block.

Pseudocode - Try Block with Except Blocks
try:
some code
except (ZeroDivisionError, RuntimeError, TypeError):
failover code

Displaying Error Text

Sometimes you want to get the actual text from an error in addition to protecting your script. There's an easy way to fetch that information that's built into Python. When you are inside an except section of code, sys.exc_info() gives you access to the error text as a list of values. You can use this to print out your message, display it on the screen, send it to the database, or anything else.

This example should be put on a button, and will write the error text to a Label component that is a sibling to the button. This is useful in Perspective to get error messages out of views and event actions.

try:
# cause an error
x=[1,2]
val = x[5]
except:
# push the error text to a sibling label
self.getSibling("Label").props.text = sys.exc_info()

You can also use the Ignition loggers to push these error messages out to the Gateway console. You can find these in the Gateway Webpage under the Status section, on the Logs page.

# This code would log an error to the gateway
try:
# cause an error
100/0
except:
# push the error text to the logger
logger = system.util.getLogger("myLogger")
# convert the sys.exc_info() to a string and log it
logger.info(str(sys.exc_info()))

Determining the Error Object

To determine the name of the error object that will be thrown from certain errors, we can take a look at the error to figure that out. We already mentioned that dividing by zero gives a ZeroDivisionError, but what about when we divide by a string? If I divide 100 by the letter a without error handling, this is the error I get:

Traceback (most recent call last):
File "buffer", line 3, in module
TypeError: unsupported operand type(s) for /: 'int' and 'str'


The last line of that error gives the name of the error object "TypeError" followed by the cause of that error. This makes sense, because the string 'a' is the wrong type to be using in division. However, not all errors are so simple and may require a bit more to find the name of the error. For a list of python error names check out this page in the python docs: https://docs.python.org/2.7/library/exceptions.html#Exception

Additionally, some exceptions may be returned by Java. In these cases, Oracle's documentation on the Exception class is more useful: https://docs.oracle.com/javase/8/docs/api/java/lang/Exception.html

A list of common exceptions are listed below.

ExceptionDescriptionException Demonstration
ArrayIndexOutOfBoundsExceptionThis typically occurs when a line of code attempts to access an index in a collection, but the index specified doesn't exist. This exception is unique to datasets in Ignition. Lists, and other built-in Python objects will return the IndexError below.
myDataset = system.dataset.toDataSet(["colName"],[[0]])

# This will fail because the dataset only has a single row,
# so trying to read the value at row index 5 means we're trying to
# read something that doesn't exist.
print myDataset.getValueAt(0,5)
AttributeError

Typically seen when a script tries to access a property that doesn't exist.

Example: a script tries to get the value of a property on a component, but the property name is misspelled.
myVar = 10

# integers do not natively have a name variable, so this will fail with an AttributeError:
# there isn't an 'attribute' on an integer by the name of 'name'
print myVar.name
IndexErrorSimilar to ArrayIndexOutOfBoundsException above, but occurs when Python specific object, such as a list.
myList = [1,2,3]

# There isn't an element in the list at index 4, so this will fail.
print myList[4]
NameErrorOccurs when the local or global name is not found. Typically happens when referencing a variable that hasn't been assigned a value.
# We haven't given a value to the variable myValue, so it will fail.
print "The value of the variable is: ", myValue
TypeErrorA TypeError occurs when a function or similar operation is applied to another object of an incorrect type.
myList = [1,2,3]

# The first argument for pop() expects an integer, so passing a string value is innappropriate.
# Passing a string to pop() will return a TypeError.
print myList.pop("0")
ValueErrorA ValueError is returned when the value of an argument is inappropriate. Typically the exact issue is returned in the returned exception.
myVar = "Hello"
# Strings can be passed to the int() function, but the value needs to be something that
# can be coerced into an integer, like "0".
# Because "Hello" can't be easily converted, the line below will fail.
print int(myVar)