Understanding Tracebacks in Python

Traceback

Traceback is the message or information or a general report along with some data, provided by Python that helps us know about an error that has occurred in our program. It’s also called raising an exception in technical terms. For any development work, error handling is one of the crucial parts when a program is being written. So, the first step in handling errors is knowing the most frequent errors we will be facing in our code.

Tracebacks provide us with a good amount of information and some messages regarding the error that occurred while running the program. Thus, it’s very important to get a general understanding of the most common errors.

Also read: Tricks for Easier Debugging in Python

Tracebacks are often referred to with certain other names like stack trace, backtrace, or stack traceback. A stack is an abstract concept in all programming languages, which just refers to a place in the system’s memory or the processor’s core where the instructions are being executed one by one. And whenever there is an error while going through the code, tracebacks try to tell us the location as well as the kind of errors it has encountered while executing those instructions.

Some of the most common Tracebacks in Python

Here’s a list of the most common tracebacks that we encounter in Python. We will also try to understand the general meaning of these errors as we move further in this article.

  • SyntaxError
  • NameError
  • IndexError
  • TypeError
  • AttributeError
  • KeyError
  • ValueError
  • ModuleNotFound and ImportError

General overview of a Traceback in Python

Before going through the most common types of tracebacks, let’s try to get an overview of the structure of a general stack trace.

# defining a function
def multiply(num1, num2):
    result = num1 * num2
    print(results)

# calling the function
multiply(10, 2)

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 6, in <module>
    multiply(10, 2)
  File "d:\Python\traceback.py", line 3, in multiply
    print(results)
NameError: name 'results' is not defined. Did you mean: 'result'?

Explanation:

Python is trying to help us out by giving us all the information about an error that has occurred while executing the program. The last line of the output says that it’s supposedly a NameError and even suggesting us a solution. Python is also trying to tell us the line number that might be the source of the error.

We can see that we have a variable name mismatch in our code. Instead of using “result”, as we earlier declared in our code, we have written “results”, which throws an error while executing the program.

So, this is the general structural hierarchy for a Traceback in Python which also implies that Python tracebacks should be read from bottom to top, which is not the case in most other programming languages.

Understanding Tracebacks

1. SyntaxError

All programming languages have their specific syntax. If we miss out on that syntax, the program will throw an error. The code has to be parsed first only then it will give us the desired output.  Thus, we have to make sure of the correct syntax for it to run correctly.

Let’s try to see the SyntaxError exception raised by Python.

# defining a function
def multiply(num1, num2):
    result = num1 * num2
    print "result"

# calling the function
multiply(10, 2)

Output:

File "d:\Python\traceback.py", line 4
    print "result"
    ^^^^^^^^^^^^^^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

Explanation:

When we try to run the above code, we see a SyntaxError exception being raised by Python. To print output in Python3.x, we need to wrap it around with a parenthesis. We can see the location of our error too, with the “^” symbol displayed below our error.

2. NameError

While writing any program, we declare variables, functions, and classes and also import modules into it. While making use of these in our program, we need to make sure that the declared things should be referenced correctly. On the contrary, if we make some kind of mistake, Python will throw an error and raise an exception.

Let’s see an example of NameError in Python.

# defining a function
def multiply(num1, num2):
    result = num1 * num2
    print(result)

# calling the function
multipli(10, 2)

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 8, in <module>
    multipli(10, 2)
NameError: name 'multipli' is not defined. Did you mean: 'multiply'?

Explanation:

Our traceback says that the name “multipli” is not defined and it’s a NameError. We have not defined the variable “multipli”, hence the error occurred.

3. IndexError

Working with indexes is a very common pattern in Python. We have to iterate over various data structures in Python to perform operations on them. Index signifies the sequence of a data structure such as a list or a tuple. Whenever we try to retrieve some kind of index data from a series or sequence which is not present in our data structure, Python throws an error saying that there is an IndexError in our code.

Let’s see an example of it.

# declaring a list
my_list = ["apple", "orange", "banana", "mango"]

# Getting the element at the index 5 from our list
print(my_list[5])

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 5, in <module>
    print(my_list[5])
IndexError: list index out of range

Explanation:

Our traceback says that we have an IndexError at line 5. It’s evident from our stack trace that our list does not contain any element at index 5, and thus it is out of range.

4. TypeError

Python throws a TypeError when trying to perform an operation or use a function with the wrong type of objects being used together in that operation.

Let’s see an example.

# declaring variables
first_num = 10
second_num = "15"

# Printing the sum
my_sum = first_num + second_num
print(my_sum)

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 6, in <module>
    my_sum = first_num + second_num
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Explanation:

In our code, we are trying to calculate the sum of two numbers. But Python is raising an exception saying that there is a TypeError for the operand “+” at line number 6. The stack trace is telling us that the addition of an integer and a string is invalid since their types do not match.

5. AttributeError

Whenever we try to access an attribute on an object which is not available on that particular object, Python throws an Attribute Error.

Let’s go through an example.

# declaring a tuple
my_tuple = (1, 2, 3, 4)

# Trying to append an element to our tuple
my_tuple.append(5)

# Print the result
print(my_tuple)

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 5, in <module>    
    my_tuple.append(5)
AttributeError: 'tuple' object has no attribute 'append'

Explanation:

Python says that there is an AttributeError for the object “tuple” at line 5. Since tuples are immutable data structures and we are trying to use the method “append” on it. Thus, there is an exception raised by Python here. Tuple objects do not have an attribute “append” as we are trying to mutate the same which is not allowed in Python.

6. KeyError

Dictionary is another data structure in Python. We use it all the time in our programs. It is composed of Key: Value pairs and we need to access those keys and values whenever required. But what happens if we try to search for a key in our dictionary which is not present?

Let’s try using a key that is not present and see what Python has to say about that.

# dictionary
my_dict = {"name": "John", "age": 54, "job": "Programmer"}

# Trying to access info from our dictionary object
get_info = my_dict["email"]

# Print the result
print(get_info)

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 5, in <module>
    get_info = my_dict["email"]
KeyError: 'email'

Explanation:

In the above example, we are trying to access the value for the key “email”. Well, Python searched for the key “email” in our dictionary object and raised an exception with a stack trace. The traceback says, there is a KeyError in our program at line 5. The provided key is nowhere to be found in the specified object, hence the error.

7. ValueError

The ValueError exception is raised by Python, whenever there is an incorrect value in a specified data type. The data type of the provided argument may be correct, but if it’s not an appropriate value, Python will throw an error for it.

Let’s see an example.

import math

# Variable declaration
my_num = -16

# Check the data type
print(f"The data type is: {type(my_num)}") # The data type is: <class 'int'>

# Trying to get the square root of our number
my_sqrt = math.sqrt(my_num)

# Print the result
print(my_sqrt)

Output:

The data type is: <class 'int'>
Traceback (most recent call last):
  File "d:\Python\traceback.py", line 10, in <module>
    my_sqrt = math.sqrt(my_num)
ValueError: math domain error

Explanation:

In the example above, we are trying to get the square root of a number using the in-built math module in Python. We are using the correct data type “int” as an argument to our function, but Python is throwing a traceback with ValueError as an exception.

This is because we can’t get a square root for a negative number, hence, it’s an incorrect value for our argument and Python tells us about the error saying that it’s a ValueError at line 10.

8. ImportError and ModuleNotFoundError

ImportError exception is raised by Python when there is an error in importing a specific module that does not exist. ModuleNotFound comes up as an exception when there is an issue with the specified path for the module which is either invalid or incorrect.

Let’s try to see these errors in action.

ImportError Example:

# Import statement
from math import addition

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 2, in <module>
    from math import addition
ImportError: cannot import name 'addition' from 'math' (unknown location)

ModuleNotFoundError Example:

import addition

Output:

Traceback (most recent call last):
  File "d:\Python\traceback.py", line 1, in <module>
    import addition
ModuleNotFoundError: No module named 'addition'

Explanation:

ModuleNotFoundError is a subclass of ImportError since both of them output similar kinds of errors and can be avoided using try and except blocks in Python.

Summary

In this article, we went through the most common types of errors or tracebacks that we encounter while writing Python code. Making mistakes or introducing a bug in any program that we write is very common for all levels of developers. Python being a very popular, user-friendly, and easy-to-use language has some great built-in tools to help us as much as it can while we develop something. Traceback is a great example of one of those tools and a fundamental concept to understand while learning Python.

Reference

traceback Documentation