Error handling with Python

Errors happen. Writing scripts that expect and handle errors can save time and frustration. When a tool returns an error message, ArcPy generates a system error or exception. In Python, you can provide a variety of structures and methods that can handle exceptions. Of course, a script can fail for other reasons not related to a geoprocessing tool. These also need to be caught and dealt with in an appropriate manner. The following sections offer a few techniques that introduce the basics of Python exception handling.

When a tool writes an error message, ArcPy generates an arcpy.ExecuteError exception. Python allows you to write a routine that automatically runs when a system error is generated. In this error-handling routine, retrieve the error message from ArcPy and react accordingly. If a script does not have an error-handling routine, it fails immediately, which decreases its robustness. Use error-handling routines to manage errors and improve a script's usability.

Geoprocessing tool error messages are accompanied by a six-digit code. These ID codes have been documented to provide additional information on their cause and how they can be dealt with.

try-except statement

A try-except statement can be used to wrap entire programs or just particular portions of code to trap and identify errors. If an error occurs within the try statement, an exception is raised, and the code under the except statement is executed. Using a basic except statement is the most basic form of error handling.

In the following code, Buffer fails because the required buffer_distance_or_field argument has not been provided. Instead of failing without explanation, the except statement is used to trap the error, then fetch and print the error message generated by Buffer. Note that the except block is only executed if Buffer returns an error.

import arcpy
import sys

try:
    # Execute the Buffer tool
    arcpy.Buffer_analysis("c:/transport/roads.shp", "c:/transport/roads_buffer.shp")
except Exception:
    e = sys.exc_info()[1]
    print(e.args[0])

    # If using this code within a script tool, AddError can be used to return messages 
    #   back to a script tool. If not, AddError will have no effect.
    arcpy.AddError(e.args[0])

The try statement has an optional finally clause that can be used for tasks that should always be executed, whether an exception occurs or not. In the following example, the ArcGIS 3D Analyst extension is checked in under a finally clause, ensuring that the extension is always checked in.

class LicenseError(Exception):
    pass

import arcpy

try:
    if arcpy.CheckExtension("3D") == "Available":
        arcpy.CheckOutExtension("3D")
    else:
        # Raise a custom exception
        raise LicenseError

    arcpy.env.workspace = "D:/GrosMorne"
    arcpy.HillShade_3d("WesternBrook", "westbrook_hill", 300)
    arcpy.Aspect_3d("WesternBrook", "westbrook_aspect")

except LicenseError:
    print "3D Analyst license is unavailable"  
except arcpy.ExecuteError:
    print(arcpy.GetMessages(2))
finally:
    # Check in the 3D Analyst extension
    arcpy.CheckInExtension("3D")

raise statement

The previous example deals with an exception that occurred in the code. In some cases, it may be necessary to create custom exceptions. A raise statement can be used for this purpose. In the following code, a raise statement is used when an input feature class has been identified as having no features. This is not strictly an error but a condition that the code can be used to guard against.

class NoFeatures(Exception):
    pass

import arcpy
import os
import sys

arcpy.env.overwriteOutput = True
fc = arcpy.GetParameterAsText(0)

try:
    # Check that the input has features
    result = arcpy.GetCount_management(fc)
    if int(result[0]) > 0:
        arcpy.FeatureToPolygon_management(
            fc, os.path.join(os.path.dirname(fc), 'out_poly.shp'))
    else:
        # Raise custom exception
        raise NoFeatures(result)

except NoFeatures:
    # The input has no features
    print('{} has no features'.format(fc))
except:
    # By default any other errors will be caught here
    e = sys.exc_info()[1]
    print(e.args[0])

ExecuteError class

When a geoprocessing tool fails, it throws an arcpy.ExecuteError exception class, which means that you can divide errors into different groups, geoprocessing errors (those that throw the arcpy.ExecuteError exception) and other exception types. You can then handle the errors differently, as demonstrated in the code below:

import arcpy
import sys

try:
    result = arcpy.GetCount_management("C:/invalid.shp")
  
# Return geoprocessing specific errors
except arcpy.ExecuteError:    
    arcpy.AddError(arcpy.GetMessages(2))    

# Return any other type of error
except:
    # By default any other errors will be caught here
    e = sys.exc_info()[1]
    print(e.args[0])

traceback

In larger, more complex scripts, it can be difficult to determine the precise location of an error. Python's sys and traceback modules can be used together to isolate the exact location and cause of the error, identifying the cause of an error more accurately and saving valuable debugging time.

# Import the required modules
#
import arcpy
import sys
import traceback

arcpy.env.workspace = "C:/Data/myData.gdb"
try:
    arcpy.CreateSpatialReference_management()
    #--------------------------
    # Your code goes here
    #
    # See the table below for examples
    #--------------------------
except arcpy.ExecuteError: 
    # Get the tool error messages 
    msgs = arcpy.GetMessages(2) 

    # Return tool error messages for use with a script tool 
    arcpy.AddError(msgs) 

    # Print tool error messages for use in Python
    print(msgs)

except:
    # Get the traceback object
    tb = sys.exc_info()[2]
    tbinfo = traceback.format_tb(tb)[0]

    # Concatenate information together concerning the error into a message string
    pymsg = "PYTHON ERRORS:\nTraceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
    msgs = "ArcPy ERRORS:\n" + arcpy.GetMessages(2) + "\n"

    # Return Python error messages for use in script tool or Python window
    arcpy.AddError(pymsg)
    arcpy.AddError(msgs)

    # Print Python error messages for use in Python / Python window
    print(pymsg)
    print(msgs)

If the above code was used and a geoprocessing tool error occurred, such an invalid input, this would raise arcpy.ExecuteError, and the first except statement would be used. This statement would print the error messages using the GetMessages function. If the same code was used, but a different type of error occurred, the second except statement would be used. Instead of printing geoprocessing messages, it gets a traceback object and prints the appropriate system error messages.

The table below shows the expected errors that result from three different lines of codes that could be substituted into the code above. The first is a geoprocessing tool error, which prints out the traceback information and the geoprocessing error messages. The second and third examples are not specifically caught and print only the traceback information.

Your codeResulting error

arcpy.GetCount_management("")

PYTHON ERRORS:
Traceback info:
  File "c:\temp\errortest.py", line 10, in <module>
    arcpy.GetCount_management("")

Error Info:
Failed to execute. Parameters are not valid.
ERROR 000735: Input Rows: value is required
Failed to execute (GetCount).

ArcPy ERRORS:
Failed to execute. Parameters are not valid.
ERROR 000735: Input Rows: value is required
Failed to execute (GetCount).

x = "a" + 1

PYTHON ERRORS:
Traceback info:
  File "c:\temp\errortest.py", line 10, in <module>

    x = "a" + 1

Error Info:
cannot concatenate 'str' and 'int' objects

float("a text string")

PYTHON ERRORS:
Traceback info:
  File "c:\temp\errortest.py", line 10, in <module>

    float("a text string")
Error Info:
invalid literal for float(): a text string

Error results

Getting error messages from a result object

A quick word about the Result object, shown below:

result = arcpy.GetCount_management("c:/data/rivers.shp")

If the call to GetCount_management raises an exception, the Result object is not created. This means you cannot retrieve error messages from the Result object.

import arcpy

try:
    result = arcpy.GetCount_management("c:/data/rivers.shp")

# Return Geoprocessing specific errors
# (this method is incorrect!)
except arcpy.ExecuteError:
    arcpy.AddError(result.getMessages(2))

The above code fails with the message name 'result' is not defined. This is because the Result object could not be created due to the tool's failure. Since the Result object is not created, a Python error is raised when trying to use the getMessages method.

Note:

A Result object created by calling a geoprocessing service on ArcGIS Server is created even with a tool failure. A Result object only fails to be created when a tool runs locally and it raises an error. For more information about using the result object, see Using tools in Python.