Python Errors and Exceptions: An Ultimate Guide to Different Types and Solutions
In this article, we’ll explore the different kinds of errors and exceptions, what causes them, and provide solutions to solving them. No more headaches and cursing your code until it gets scared and starts working – master the language of Python to understand precisely what it wants from you.
What is the difference between Python errors and exceptions?
The terms errors and exceptions are used interchangeably when talking about Python. They refer to any kind of mistake or issue that prevents the code from running successfully. While they’ll often mean the same thing, there are a few differences between them.
Errors refer to a broad definition of issues when running Python code. They can range from simple mistakes where you’ve forgotten to put a bracket to runtime errors that indicate a bigger problem, like a missing library. An error prevents your script from running smoothly, so if you didn’t get the desired result, you probably got an error.
Exceptions refer to runtime errors in particular. They are events that occur during the execution of a program that disrupt the normal flow of the program’s instructions. Unlike errors, they don’t mean that the code must stop completely. Python offers the ability to write further instructions to handle the exception and continue running the program smoothly.
What are the different types of Python errors?
Python errors come in many different shapes and sizes, but they can all be categorized into three distinct types: syntax errors, logical errors, and runtime errors (exceptions).
- Syntax errors occur when your code doesn’t meet the Python syntax rules. This can be a missing parenthesis, an extra symbol, too many quotation marks, etc. They happen due to human mistakes, so there’s no need to panic – the interpreter is simply confused about what you want to do. It will not run the code until you find and fix the issue. Example:
# SyntaxError - missing second quotation mark (")print("Hello world!)
- Logical errors happen when everything goes smoothly, but the outcome isn’t as expected. These don’t produce any warnings; the code will execute as it’s told to do without issues. Avoiding logical errors requires a keen eye and plenty of automated tests to ensure you get precisely what you want. Example:
# Expected result: 8. Code returns 12, because a plus sign is used instead of a minusresult = 10 + 2print(result)
- Runtime errors, also known as exceptions, occur during the execution of a program. They check if all conditions of a well-structured code are met and watch if any unexpected events occur. Python has many built-in exceptions that you’ll most likely run into, as well as the ability to write user-defined exceptions. Fixing them requires modifying your code to meet these requirements to run the program smoothly. You may also write exception handlers to execute specific actions when an exception occurs to ensure your application runs smoothly and doesn’t crash. Example:
# NameError - "id" was not defined as a variable anywhere in the coderesult = 10 + idprint(result)
Regardless of which error you face, they are detrimental if you want to write robust code and should be handled immediately. Syntax and logic errors require you to fix them manually – either by hand or by running tests to test all possible outcomes.
Runtime errors can be dealt with more cleanly and concisely through exception handling. They can be true saviors of your code, as they won’t stop your application from running and will execute specific actions when troubles occur. Read on to learn more about them.
What are Python exceptions?
When a Python script encounters an error, it raises an exception. Exceptions are used to handle errors or unexpected situations in a program, preventing it from crashing and providing a way to handle errors in a controlled manner.
How do you handle Python exceptions?
The most common way of Python exception handling is the try-except method. A part of a code that may raise an exception is “tested” with a try clause, and if it returns an exception, the except block will run instead. Here’s a simple example:
try:# Code that may raise an exceptionresult = 5 / 0 # This will raise a ZeroDivisionErrorexcept ZeroDivisionError:# Code to handle the exceptionprint("Error: can't divide by 0.")
In an actual application, the except code block would run a specific code to handle the error and ensure smooth further execution of the application without stopping it.
You can also list multiple exceptions and provide different methods of handling each one. This way, you have complete control over how your program behaves in any given situation:
try:# Code that may raise an exceptionresult = 5 / i # This will raise a NameErrorexcept ZeroDivisionError:# Code to handle the exceptionprint("Error: can't divide by 0.")except NameError:# Code to handle the exceptionprint("Error: variable was not declared.")
An alternative method of handling exceptions is simply to write a general exception handler that will catch any exception regardless of its type. This method, however, isn’t recommended, and it’s suggested to be as specific as possible with how individual exceptions are handled:
try:# Code that may raise an exceptionresult = 5 / i # This will raise a NameErrorexcept Exception as e:# Code to handle the exceptionprint(f"An error occurred: {e}")
Lastly, you can use the else and finally code blocks together with the try-except structure. The else block is executed if no exceptions are raised, and the finally block will execute regardless of what happens. It’s just too cool to care.
try:# Code that may raise an exceptionresult = 5 / 2except ZeroDivisionError:# Code to handle the exceptionprint("Error: can't divide by 0.")else:# Code to execute if no exception occurredprint(f"Result: {result}")finally:# Code that always executesprint("You can't contain me.")
These four code blocks are enough to handle any exceptions thrown your way. It ensures that the code has alternate paths to take no matter what happens and that your code won’t break when you least expect it.
Learning what they all mean is essential to handle each error correctly and know what to do. Check out the most common built-in exceptions listed below.
KeyError
Description: A KeyError exception is raised when you try to access a key not found in the dictionary. In simple terms, you’re attempting to use something from the dictionary (array) that doesn’t exist or hasn’t been defined yet.
Example:
# The dictionary only has 3 keys - a, b, and cmyDictionary = {'a':1, 'b':2, 'c':3}# Attempt to print d which was not defined in the dictionary throws a KeyError exceptionprint(myDictionary['d'])
Solution: There can be many reasons a key is missing, so the most straightforward answer would be to add it to the dictionary manually. If the dictionary is created dynamically by reading data from a source, ensure the source has the required information. Finally, guarantee that the names match and that there aren’t any typos in the key name in the dictionary and the code that’s trying to use it.
ImportError
Description: The ImportError is raised when a module isn’t found, doesn’t exist, is corrupted or incomplete, or there’s a circular dependency between two or more modules.
Since Python 3.6, a more accurate and informative subclass of the ImportError exception was introduced – ModuleNotFoundError. It covers most cases when dealing with module imports but doesn’t cover circular dependencies.
Example:
# module_a.pyfrom module_b import b_functiondef a_function():b_function()
# module_b.pyfrom module_a import a_functiondef b_function():a_function()
The a_function() in module_a.py is trying to use the b_function() from module_b.py. The b_function() in module_b.py needs the a_function() from module_a.py. This is a classic dilemma of who came first – the chicken or the egg? Python definitely doesn’t have the answer to this philosophical question.
Solution: Reorganize your code to move shared functionality into a third module that both module_a.py and module_b.py can import without creating a circular dependency.
# shared_module.pydef shared_function():print("Shared function")
# module_a.pyfrom shared_module import shared_functiondef a_function():shared_function()a_function()
# module_b.pyfrom shared_module import shared_functiondef b_function():shared_function()b_function()
ModuleNotFoundError
Description: This exception is a specific type of ImportError that comes up when you’re trying to import a non-existent module or when it’s defined incorrectly in your import statement.
Example:
# Import the platforms module. It's not a built-in Python module, or exists as a fileimport platforms# Attempt to use the module and one of its functions throws a ModuleNotFoundErroruserSystem = platforms.system()print(userSystem)
Solution: Make sure that the module exists. See if it’s in the official Python built-in module index, or check your file directory to ensure the file is there (all modules must end with a .py extension). Alternatively, see if you’ve made a typo in the module name. It’s important to remember that the names are case-sensitive, so check that the capitalization is correct.
SyntaxError
Description: The SyntaxError exception occurs when there’s a mistake in how the code is written, and the interpreter can’t parse and execute it. The most common causes include missing or mismatched parentheses, brackets, colons, and incorrect indentation.
Example:
print("Hello, World!" # Missing parentheses
Solution: Go back to review the code and fix any syntax issues. The exception provides a traceback that points to the location in the code where the error was made, making it easy to find. Remember that the interpreter might not always be 100% accurate, and the error might be located a line above or below where it’s indicated.
NameError
Description: This exception is raised when the interpreter can’t find a variable, function, or module with the given name. Simply put, it’s unsure what you’re referencing in the current scope, meaning that you didn’t define something earlier before using it.
Example:
# Attempt to print a variableprint(variable)# The variable in question, declared only after the print functionvariable = 5
Solution: Check for typos, declare variables before use, and ensure that functions or classes are defined before they are called.
TypeError
Description: An exception occurs when an operation or function is applied to an object of an inappropriate type. These are primarily logical errors, such as trying to add a numeric value with a string value or trying to find the length of a number.
Example:
# Trying to add a string value with a numeric valueresult = "10" + 5print(result)
Solution: Check if all operations and functions are interacting only with objects that are compatible with them in type. You may also convert objects using functions such as int() or str() that will change the object type to a number or string, respectively.
IndexError
Description: The IndexError exception occurs when you try to access an index outside a given range. For example, an array might contain five items, but you’re attempting to access the 7th item, which doesn’t exist.
Example:
# Array of itemsmy_list = [1, 2, 3]# Attempt to print the 5th itemprint(my_list[5])
Solution: To handle the IndexError exception class, avoid accessing indexes not in a given range. A standard solution is to check the bounds of a sequence first with functions such as len(). It’s important not to forget that in Python, most sequences will start at 0 and not 1, meaning that the first item will have an index of 0, and the last will be one number lower than the length of the entire sequence.
AttributeError
Description: In Python, attributes are values associated with an object. They are characteristics or properties that can be accessed using dot notation (object.attribute) and provide information about the object or its current state.
An AttributeError may occur when trying to access or modify an attribute of an object that doesn’t exist or isn’t accessible. In other words, the specified attribute doesn’t fit and can’t be applied to the given object.
Example:
# The object is defined as an integer 5, which doesn't have an attribute "length"my_object = 5print(my_object.length)
Solution: To handle AttributeError issues, ensure that attributes accessed or modified are valid for the given object. It’s good to check the documentation of an object you’re using or use the hasattr() function to check if an attribute exists. For example:
my_object = 5# Check if the object has an attribute called "length"if hasattr(my_object, 'length'):# Do something if it has itelse:# Do something if it doesn't
FileNotFoundError
Description: The FileNotFound error message typically occurs in file I/O operations when trying to open, read, or write files. The exception instance is raised when attempting to access a file or directory unsuccessfully because it’s either not found or inaccessible.
Example:
# Attempt to open tells that such file that doesn't existwith open('non_existent_file.txt', 'r') as file:content = file.read()
Solution: Ensure that such a file exists in the directory. A common mistake is writing the wrong path to the directory from the script file or a typo somewhere in the path or file name. The file has to also be in a readable format that your application can understand. By default, Python can read or open many variations of text, database, CSV, JSON, and XML files, expanded by the libraries that may allow even more possibilities. Double-check if you’ve got the right tools.
ZeroDivisionError
Description: The ZeroDivisionError exception simply signifies an attempt to divide by zero. This operation is undefined in mathematics and doesn’t provide any sensible results; therefore, it doesn’t make sense in code either.
Example:
result = 10 / 0print(result)
Solution: Double-check your code for zero denominators before performing any division operations.
ValueError
Description: The ValueError exception is raised when a function receives an argument of the correct type but with an invalid value. This usually happens when the input isn’t within an expected range or doesn’t satisfy certain conditions.
Example:
# Attempt to convert a float value (10.5) to a decimal value (base 10)x = int("10.5")print(x)
Solution: The best way to handle this error is to validate input values before they’re used in operations or functions. You can use conditional statements or try-except blocks to catch and handle the ValueError exceptions cleanly and consistently.
Best practices for error handling
While many errors require different handling methods, you may notice some similarities. Here are some of the most common things to keep in mind:
- Check for typos. The most common errors are human errors, so before turning to any complex solutions, take a step back and review your code. A scary error message might indicate a missing semicolon or a mistyped word. Most Python exceptions will provide a traceback, pointing to exactly where the error was made.
- Validate your data. Before plugging your data into a Python script, ensure it’s correct and makes sense. Many issues come from data that’s poorly formatted or doesn’t meet the requirements of a function. Use conditional statements to check if the information provided is within bounds and fits the action you’re trying to do with it.
- Use the try-except blocks. This is the big one. As Python provides a graceful and easy way of handling exceptions, you should take full advantage of it. If there’s a risk that your code might fail at any given line, wrap it into a try block and provide except to handle any exceptions you might expect. Expand it with else and finally blocks to provide your code more than one way out of an issue.
Final words
This article covered the general knowledge of what errors and exceptions you can get in Python and a list of the most common ones so you know exactly how to deal with them. Writing clean and concise code can be an art form, so don’t worry about running into issues – they’re there to help you and guide you through. Knowing Python's different exceptions and errors allows you to take complete control of any situation and succeed in any coding project.
About the author
Zilvinas Tamulis
Technical Copywriter
Zilvinas is an experienced technical copywriter specializing in web development and network technologies. With extensive proxy and web scraping knowledge, he’s eager to share valuable insights and practical tips for confidently navigating the digital world.
All information on Smartproxy Blog is provided on an "as is" basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Smartproxy Blog or any third-party websites that may be linked therein.