IIt's important to understand what your application's error type is telling you when debugging an issue.
In this article, we'll cover common Python error types and what your application tells you when encountering one.
Error messages are most often output in the following format:
File, line 1, Context ErrorType: error message
For example, considering the following example:
File "/../maths_is_hard.py", line 1, in <module> print(f"{x} divided by {y} is {x / y}") NameError: name 'x' is not defined
We can deduce the following:
Which can be understood as: "On line 1, of the maths_is_hard.py file, an undefined x
variable was used."
Whenever an error occurs in your application, Python will raise an exception and log a stack trace, which you can then use to find the location of an error within your application's code.
In Python, if you try and divide a number by zero, you will raise a ZeroDivisionError
. To demonstrate a stack trace, let's raise one:
# maths_is_hard.py def divide(x, y): print(f"{x} divided by {y} is {x / y}") divide(8, 0)
In the above code snippet, we've created a function called divide(a, b)
, which will divide any two integers we provide it. Let's run the code in our terminal to raise ZeroDivisionError
and print a stack trace:
$ python maths_is_hard.py Traceback (most recent call last): File "/home/runner/python-test-repo/maths_is_hard.py", line 8, in <module> divide(8, 0) File "/home/runner/python-test-repo/maths_is_hard.py", line 3, in divide print(f"{x} divided by {y} is {x / y}") ZeroDivisionError: division by zero
So, what is our stack trace telling us? Let's look at it line per line:
maths_is_hard.py
code was executed that resulted in a ZeroDivisionError, below the line it shows us the function: divide(0,8)
maths_is_hard.py
file via the divide function. Indented, under, it shows us the code of that line.We can use this stack trace to deduce where the error was raised in our code and where the method was called from. In the case of the example above, we know the issue was caused by 0
being passed as an argument on maths_is_hard.py:3
. We could then make sure not to pass 0
as an argument or modify our method to safely return a message if 0
is passed as an integer:
# maths_is_hard.py def divide(x, y): if y > 0: print(f"{x} divided by {y} is {x / y}") else: print("You can't divide by 0") divide(8, 0)
Which would output the following when run:
$ python maths_is_hard.py You can't divide by 0
Good to know: We're using shorter stack traces here for brevity, but your application's stack trace will often be much longer. However, the same logic to reading stack traces applies, regardless of length.
A KeyError
occurs when trying to access a key that does not exist with dict[key]
, for example, when trying to access the non-defined indian
key in the below python_breed_count
dict:
> python_breed_count = { 'ball': 12, 'timor': 9, 'burmese': 2 } > python_breed_count['indian'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'indian'
in
operatorWe can use the in
operator to check to see if a value is in a dict
, and then execute specific logic when that condition is met:
if 'burmese' in python_breed_count: print(f"There are {python_breed_count['burmese']} snakes in the plane")
Dict
You can always list the available keys using the keys()
function:
> python_breed_count.keys() dict_keys(['ball', 'timor', 'burmese'])
You could then use dict[key]
to retrieve available key values:
> python_breed_count['burmese'] 2
However, as the KeyError example shows, an exception is raised when a key the dict does not contain is called:
> python_breed_count['carpet'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'carpet'
If we don't wish to raise an error in this use case, we can instead dict(key)
to retrieve values. When a key is not present, none
or a default value:
> python_breed_count = { 'ball': 12, 'timor': 9, 'burmese': 2 } # does not throw error, returns None if the key is not present > python_breed_count.get('indian') # does not throw error, returns 0 if the key is not present > python_breed_count.get('indian', 0) 0
A NameError
is raised when calling an attribute or function that is not defined.
For example, we want to assign a string to the attribute, title
only we make a typo and accidentally assign it to an attribute incorrectly named titel
, meaning when we call title
in our code, we raise a NameError
, because title
is not defined.
> titel = "Monty's Masterclass: Pythonic Puns and Pop Culture Parodies" > title Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'title' is not defined. Did you mean: 'titel'?
To resolve this error, we need to make sure we are calling attributes and functions that are defined:
> title = "Monty's Masterclass: Pythonic Puns and Pop Culture Parodies" > title "Monty's Masterclass: Pythonic Puns and Pop Culture Parodies"
UnicodeEncodeError
and UnicodeDecodeError
are raised when Python cannot encode or decode from or to Unicode.
In the below example, we're trying to encode the string piñata
to ASCII
. Because ASCII
codec does not support the ñ
character a UnicodeEncodeError
is raised:
> partyGame = "Piñata" > encodedPartyGame = partyGame.encode('ascii') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode character '\xf1' in position 2: ordinal not in range(128)
As you can see in the above error, ñ
, known in unicode as /xf1
cannot be encoded.
A solution is to use a codec that supports the character you want to encode. Let's try in UTF-8:
> partyGame.encode('utf-8') b'Pi\xc3\xb1ata'
As you can see, piñata is successfully encoded into a UTF-8 byte string. Let's try and decode it:
encodedPartyGame.decode('utf-8') 'Piñata'
We may, however, also encounter a situation where you need to decode a byte_string via a specific codec and encounter an error due to an unsupported character:
> encodedPartyGame.decode('ascii') UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2: ordinal not in range(128)
In this case, we can pass an error
argument telling Python what to do when encountering a character it can't decode. The values are strict
, replace
, ignore
, and backslashreplace
. While strict
will return a UnicodeDecodeError
the other arguments allow you to handle the encoded character without raising an error:
> encodedPartyGame.decode('ascii', 'replace') 'Pi��ata' > encodedPartyGame.decode('ascii', 'ignore') 'Piata' > encodedPartyGame.decode('ascii', 'backslashreplace') 'Pi\\xc3\\xb1ata'
We can also try replacing non-supported characters before encoding:
> 'piñata'.replace('ñ', 'n').encode('ASCII') b'Pinata'
A TypeError
occurs when an object is the wrong type for the action you are attempting to execute. For example, when trying to add an integer to a string:
> "1" + 2 TypeError: can only concatenate str (not "int") to str
As 1 is a string, it has no numerical value and can't be added to another integer. To be added successfully, it must first be converted into an integer, and we can do this using Python's int()
function.
> int('1') + 2 3
Note that we can only convert textual representations of integers to integer objects, not strings. Trying to convert a string to an integer would raise a ValueError
.
A ValueError
occurs when a function is called with the correct argument type but the wrong value.
For example, Python's int()
function, which converts strings of integers into integer objects, expects a string
argument. However, the string must be a textual representation of an integer:
> int('one') ValueError: invalid literal for int() with base 10: 'one'
In this case, to resolve the error, we need to make sure we pass the correct value to the int()
function:
> int('1') 1
If you need clarification on what values a function requires, access the function in your application or read the relevant documentation. For example, when looking at Python's documentation for the int()
function we can read that by default, the function uses base 10, meaning the string can only contain digits between 0-9.
With AppSignal Error Reporting, AppSignal can record and notify you when errors occur, helping you quickly resolve problems in your application's codebase.
AppSignal's Error Reporting is just one of our many developer-driven features that help you get the most out of monitoring your application. Developers also enjoy using our monitoring because we offer:
If you're ready to monitor your application's errors, start a free trial with AppSignal today!
AppSignal offers a 30-day free trial, no credit card is required. All features are available in all plans. Start monitoring your application in just a few clicks!