Skip to content

Comprehensive Guide Common Built-in Exceptions Python

Updated: at 04:56 AM

Python provides many built-in exceptions that are raised when certain errors occur during the execution of a program. Understanding these common exceptions, when they are raised, and how to handle them is an important part of writing robust Python code. This guide will provide an overview of some of the most frequently encountered built-in exceptions in Python.

Table of Contents

Open Table of Contents

Overview of Built-in Exceptions

All built-in exceptions in Python inherit from the base Exception class. When an error occurs, Python will create an exception object containing information about the error and will raise it in the form of an exception.

Built-in exceptions can be grouped into several broad categories:

Below we will explore some of the most common built-in exceptions in more detail.

IOError and OSError

IOError and OSError are very common exceptions raised when an input/output operation fails, such as trying to open a file that doesn’t exist or trying to work with a closed file.

IOError is used for I/O failures involving the Python built-in open() function and streams. In Python 3, IOError has been renamed to OSError and handles all system-related errors.

For example:

try:
    f = open('file.txt')
except IOError:
    print("Could not open file.txt")

If ‘file.txt’ does not exist, an IOError will be raised and the exception handler will print the error message.

OSError is a broader category of exceptions that include things like missing files, full disks, permissions issues, damaged disks, etc.

For example:

import os
try:
    os.remove('file.txt')
except OSError:
    print("Error deleting file.txt")

If ‘file.txt’ is being used by another process, an OSError may be raised.

To handle these exceptions, it’s good practice to catch both IOError and OSError since the latter subsumes the former in Python 3:

try:
    open('file.txt')
except (IOError, OSError):
    print("Error opening file!")

ValueError and TypeError

The ValueError and TypeError exceptions occur when you try to perform an operation on the wrong type of object.

ValueError is raised when the type of object is expected, but the value is inappropriate.

For example:

import math

x = 'hello'
try:
    print(math.sqrt(x))
except ValueError:
    print("Cannot compute square root of string.")

Here a string is passed instead of a number, causing a ValueError.

TypeError is raised when the object type is not appropriate for the operation being attempted.

For example:

x = 5
try:
    print(x + '10')
except TypeError:
    print("Cannot concat integer and string.")

This raises a TypeError because you can’t add an integer and string together using +.

To avoid these errors, you should:

Handling these exceptions gracefully allows your program to recover and continue execution when invalid values or object types are encountered.

AttributeError and NameError

The AttributeError and NameError exceptions are raised when code tries to access attributes or names that do not exist.

AttributeError is raised when you try to access an attribute that is not defined on an object.

For example:

class Person:
    pass

p = Person()
try:
    p.name
except AttributeError:
    print("Person object has no attribute 'name'")

Since Person class did not define a name attribute, referencing p.name raises an AttributeError.

NameError occurs when a local or global name is not found. For example:

try:
    print(some_unknown_var)
except NameError:
    print("Name 'some_unknown_var' is not defined")

Trying to print a variable some_unknown_var that is not defined raises a NameError.

These exceptions can be avoided by:

KeyError and IndexError

KeyError and IndexError are raised when trying to access keys or indices on data structures like dictionaries and lists that do not exist.

KeyError is raised when trying to access a dictionary key that does not exist.

For example:

d = {'name': 'John'}
try:
    print(d['age'])
except KeyError:
    print("Key 'age' not found in dict")

Since 'age' is not a key in the d dictionary, trying to access d['age'] raises a KeyError.

IndexError occurs when trying to access a list index that is out of bounds.

For example:

a = [1, 2, 3]
try:
    print(a[3])
except IndexError:
    print("List index out of range")

Here we try to print a[3] but the valid indices are 0 through 2, so it raises an IndexError.

These issues can be prevented by:

AssertionError

An AssertionError exception is raised when an assert statement fails.

assert is used to check if a condition is true and triggers an AssertionError if it evaluates to False.

For example:

x = 5
assert x == 5 # Passes

x = 10
assert x == 5 # Fails, raises AssertionError

Here the second assert checks if x == 5 which evaluates to False, so an AssertionError is raised.

AssertionErrors are typically handled by debugging the issue first. But you can catch and handle them as well:

try:
   assert x == 5
except AssertionError:
   print("Assertion failed!")

Using assert allows you to sanity check expectations in your code. Mishandled assertions can potentially lead to bugs, so these exceptions should be addressed.

ImportError and ModuleNotFoundError

The ImportError and ModuleNotFoundError arise when there are issues importing a module.

ImportError is the base exception for module/import related issues. For example:

try:
    import somemodule
except ImportError:
    print("Could not import somemodule")

This catches any generic failure to import a module.

ModuleNotFoundError is new in Python 3 and is a subclass of ImportError. It’s raised when a module is not found.

For example:

try:
    import somemodule
except ModuleNotFoundError:
    print("No module named 'somemodule' found")

This specifically catches when the module itself is unavailable.

Some reasons these exceptions occur:

To fix import errors:

Handling Multiple Exceptions

You can handle multiple exceptions using the same except block:

try:
    # Code that may raise exceptions
except (ValueError, TypeError):
    # Handles ValueError and TypeError

You can also chain multiple except blocks to handle specific exceptions differently:

try:
    # Code
except ValueError:
    # Handle ValueError
except TypeError:
    # Handle TypeError

It’s good practice to order except blocks from most specific to most general.

Raising Exceptions

In addition to built-in exceptions, you can raise your own exceptions using the raise statement:

raise Exception('An error occurred')

After raising it, this exception can be caught and handled like any other exception:

try:
   raise Exception('Error!')
except Exception as e:
   print(e)

You can define custom exception classes that inherit from Exception to provide added context.

For example:

class CustomError(Exception):
    pass

raise CustomError("Custom error occurred")

Being able to raise your own exceptions is useful for signaling errors from your functions, classes, and modules.

Best Practices

Here are some best practices when dealing with exceptions in Python:

Properly handling exceptions helps make Python programs more robust, user friendly, and secure. Mastering built-in exceptions is key to writing stable production-level code.

Conclusion

Python’s built-in exceptions serve an important role in signaling errors and anomalous conditions. Some of the most common exceptions include IOError, ValueError, KeyError, AttributeError, TypeError, and ImportError.

Knowing when these exceptions are raised and how to handle them properly ensures your Python programs are fault-tolerant and able to gracefully recover from exceptions. Exception handling is an essential skill for any Python developer.

This guide covered a variety of the core exceptions, when they occur, how to handle them through catching and raising exceptions. Ensure you leverage exception handling in your Python code to write stable and resilient programs.