In this article, you will learn error and exception handling in Python.
By the end of the article, you’ll know:
- How to handle exceptions using the try, except, and finally statements
- How to create a custom exception
- How to raise an exceptions
- How to use built-in exception effectively to build robust Python programs

Table of contents
What are Exceptions?
An exception is an event that occurs during the execution of programs that disrupt the normal flow of execution (e.g., KeyError Raised when a key is not found in a dictionary.) An exception is a Python object that represents an error..
In Python, an exception is an object derives from the BaseException class that contains information about an error event that occurred within a method. Exception object contains:
- Error type (exception name)
- The state of the program when the error occurred
- An error message describes the error event.
Exception are useful to indicate different types of possible failure condition.
For example, bellow are the few standard exceptions
- FileNotFoundException
- ImportError
- RuntimeError
- NameError
- TypeError
In Python, we can throw an exception in the try block and catch it in except block.
Why use Exception
- Standardized error handling: Using built-in exceptions or creating a custom exception with a more precise name and description, you can adequately define the error event, which helps you debug the error event.
- Cleaner code: Exceptions separate the error-handling code from regular code, which helps us to maintain large code easily.
- Robust application: With the help of exceptions, we can develop a solid application, which can handle error event efficiently
- Exceptions propagation: By default, the exception propagates the call stack if you don’t catch it. For example, if any error event occurred in a nested function, you do not have to explicitly catch-and-forward it; automatically, it gets forwarded to the calling function where you can handle it.
- Different error types: Either you can use built-in exception or create your custom exception and group them by their generalized parent class, or Differentiate errors by their actual class
What are Errors?
On the other hand, An error is an action that is incorrect or inaccurate. For example, syntax error. Due to which the program fails to execute.
The errors can be broadly classified into two types:
- Syntax errors
- Logical errors
Syntax error
The syntax error occurs when we are not following the proper structure or syntax of the language. A syntax error is also known as a parsing error.
When Python parses the program and finds an incorrect statement it is known as a syntax error. When the parser found a syntax error it exits with an error message without running anything.
Common Python Syntax errors:
- Incorrect indentation
- Missing colon, comma, or brackets
- Putting keywords in the wrong place.
Example
print("Welcome to PYnative")
print("Learn Python with us..")
Output
print("Learn Python with us..")
^
IndentationError: unexpected indent
Logical errors (Exception)
Even if a statement or expression is syntactically correct, the error that occurs at the runtime is known as a Logical error or Exception. In other words, Errors detected during execution are called exceptions.
Common Python Logical errors:
- Indenting a block to the wrong level
- using the wrong variable name
- making a mistake in a boolean expression
Example
a = 10
b = 20
print("Addition:", a + c)
Output
print("Addition:", a + c)
NameError: name 'c' is not defined
Built-in Exceptions
The below table shows different built-in exceptions.
Python automatically generates many exceptions and errors. Runtime exceptions, generally a result of programming errors, such as:
- Reading a file that is not present
- Trying to read data outside the available index of a list
- Dividing an integer value by zero
Exception | Description |
---|---|
AssertionError | Raised when an assert statement fails. |
AttributeError | Raised when attribute assignment or reference fails. |
EOFError | Raised when the input() function hits the end-of-file condition. |
FloatingPointError | Raised when a floating-point operation fails. |
GeneratorExit | Raise when a generator’s close() method is called. |
ImportError | Raised when the imported module is not found. |
IndexError | Raised when the index of a sequence is out of range. |
KeyError | Raised when a key is not found in a dictionary. |
KeyboardInterrupt | Raised when the user hits the interrupt key (Ctrl+C or Delete) |
MemoryError | Raised when an operation runs out of memory. |
NameError | Raised when a variable is not found in the local or global scope. |
OSError | Raised when system operation causes system related error. |
ReferenceError | Raised when a weak reference proxy is used to access a garbage collected referent. |
Example: The FilenotfoundError
is raised when a file in not present on the disk
fp = open("test.txt", "r")
if fp:
print("file is opened successfully")
Output:
FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'
The try
and except
Block to Handling Exceptions
When an exception occurs, Python stops the program execution and generates an exception message. It is highly recommended to handle exceptions. The doubtful code that may raise an exception is called risky code.
To handle exceptions we need to use try and except block. Define risky code that can raise an exception inside the try
block and corresponding handling code inside the except
block.
Syntax
try :
# statements in try block
except :
# executed when exception occured in try block

The try block is for risky code that can raise an exception and the except block to handle error raised in a try block. For example, if we divide any number by zero, try block will throw ZeroDivisionError
, so we should handle that exception in the except block.
When we do not use try…except
block in the program, the program terminates abnormally, or it will be nongraceful termination of the program.
Now let’s see the example when we do not use try…except
block for handling exceptions.
Example:
a = 10
b = 0
c = a / b
print("a/b = %d" % c)
Output
Traceback (most recent call last):
File "E:/demos/exception.py", line 3, in <module>
c = a / b
ZeroDivisionError: division by zero
We can see in the above code when we are divided by 0; Python throws an exception as ZeroDivisionError
and the program terminated abnormally.
We can handle the above exception using the try…except
block. See the following code.
Example
try:
a = 10
b = 0
c = a/b
print("The answer of a divide by b:", c)
except:
print("Can't divide with zero. Provide different number")
Output
Can't divide with zero. Provide different number
Catching Specific Exceptions
We can also catch a specific exception. In the above example, we did not mention any specific exception in the except block. Catch all the exceptions and handle every exception is not good programming practice.
It is good practice to specify an exact exception that the except clause should catch. For example, to catch an exception that occurs when the user enters a non-numerical value instead of a number, we can catch only the built-in ValueError exception that will handle such an event properly.
We can specify which exception except
block should catch or handle. A try
block can be followed by multiple numbers of except
blocks to handle the different exceptions. But only one exception will be executed when an exception occurs.
Example
In this example, we will ask the user for the denominator value. If the user enters a number, the program will evaluate and produce the result.
If the user enters a non-numeric value then, the try block will throw a ValueError
exception, and we can catch that using a first catch block ‘except ValueError’ by printing the message ‘Entered value is wrong’.
And suppose the user enters the denominator as zero. In that case, the try block will throw a ZeroDivisionError
, and we can catch that using a second catch block by printing the message ‘Can’t divide by zero’.
try:
a = int(input("Enter value of a:"))
b = int(input("Enter value of b:"))
c = a/b
print("The answer of a divide by b:", c)
except ValueError:
print("Entered value is wrong")
except ZeroDivisionError:
print("Can't divide by zero")
Output 1:
Enter value of a:Ten Entered value is wrong
Output 2:
Enter value of a:10 Enter value of b:0 Can't divide by zero
Output 3:
Enter value of a:10 Enter value of b:2 The answer of a divide by b: 5.0
Handle multiple exceptions with a single except clause
We can also handle multiple exceptions with a single except
clause. For that, we can use an tuple
of values to specify multiple exceptions in an except
clause.
Example
Let’s see how to specifiy two exceptions in the single except clause.
try:
a = int(input("Enter value of a:"))
b = int(input("Enter value of b:"))
c = a / b
print("The answer of a divide by b:", c)
except(ValueError, ZeroDivisionError):
print("Please enter a valid value")
Using try
with finally
Python provides the finally
block, which is used with the try block statement. The finally
block is used to write a block of code that must execute, whether the try
block raises an error or not.
Mainly, the finally
block is used to release the external resource. This block provides a guarantee of execution.

Clean-up actions using finally
Sometimes we want to execute some action at any cost, even if an error occurred in a program. In Python, we can perform such actions using a finally statement with a try and except statement.
The block of code written in the finally block will always execute even there is an exception in the try and except block.
If an exception is not handled by except clause, then finally block executes first, then the exception is thrown. This process is known as clean-up action.
Syntax
try:
# block of code
# this may throw an exception
finally:
# block of code
# this will always be executed
# after the try and any except block
Example
try:
a = int(input("Enter value of a:"))
b = int(input("Enter value of b:"))
c = a / b
print("The answer of a divide by b:", c)
except ZeroDivisionError:
print("Can't divide with zero")
finally:
print("Inside a finally block")
Output 1:
Enter value of a:20 Enter value of b:5 The answer of a divide by b: 4.0 Inside a finally block
Output 2:
Enter value of a:20 Enter value of b:0 Can't divide with zero Inside a finally block
In the above example, we can see we divide a number by 0 and get an error, and the program terminates normally. In this case, the finally
block was also executed.
Using try
with else
clause
Sometimes we might want to run a specific block of code. In that case, we can use else
block with the try-except
block. The else
block will be executed if and only if there are no exception is the try
block. For these cases, we can use the optional else
statement with the try
statement.
Why to use else
block with try?
Use else statemen with try block to check if try block executed without any exception or if you want to run a specific code only if an exception is not raised

Syntax
try:
# block of code
except Exception1:
# block of code
else:
# this code executes when exceptions not occured
try
: Thetry
block for risky code that can throw an exception.except
: Theexcept
block to handle error raised in atry
block.else
: Theelse
block is executed if there is no exception.
Example
try:
a = int(input("Enter value of a:"))
b = int(input("Enter value of b:"))
c = a / b
print("a/b = %d" % c)
except ZeroDivisionError:
print("Can't divide by zero")
else:
print("We are in else block ")
Output 1
Enter value of a: 20 Enter value of b:4 a/b = 5 We are in else block
Output 2
Enter value of a: 20 Enter value of b:0 Can't divide by zero
Raising an Exceptions
In Python, the raise
statement allows us to throw an exception. The single arguments in the raise
statement show an exception to be raised. This can be either an exception object or an Exception
class that is derived from the Exception
class.
The raise
statement is useful in situations where we need to raise an exception to the caller program. We can raise exceptions in cases such as wrong data received or any validation failure.
Follow the below steps to raise an exception:
- Create an exception of the appropriate type. Use the existing built-in exceptions or create your won exception as per the requirement.
- Pass the appropriate data while raising an exception.
- Execute a raise statement, by providing the exception class.
The syntax to use the raise
statement is given below.
raise Exception_class,<value>
Example
In this example, we will throw an exception if interest rate is greater than 100.
def simple_interest(amount, year, rate):
try:
if rate > 100:
raise ValueError(rate)
interest = (amount * year * rate) / 100
print('The Simple Interest is', interest)
return interest
except ValueError:
print('interest rate is out of range', rate)
print('Case 1')
simple_interest(800, 6, 8)
print('Case 2')
simple_interest(800, 6, 800)
Output:
Case 1 The Simple Interest is 384.0 Case 2 interest rate is out of range 800
Exception Chaining
The exception chaining is available only in Python 3. The raise
statements allow us as optional from
statement, which enables chaining exceptions. So we can implement exception chaining in python3 by using raise…from
clause to chain exception.
When exception raise, exception chaining happens automatically. The exception can be raised inside except
or finally
block section. We also disabled exception chaining by using from None
idiom.
Example
try:
a = int(input("Enter value of a:"))
b = int(input("Enter value of b:"))
c = a/b
print("The answer of a divide by b:", c)
except ZeroDivisionError as e:
raise ValueError("Division failed") from e
# Output: Enter value of a:10
# Enter value of b:0
# ValueError: Division failed
In the above example, we use exception chaining using raise...from
clause and raise ValueError
division failed.
Custom and User-defined Exceptions
Sometimes we have to define and raise
exceptions explicitly to indicate that something goes wrong. Such a type of exception is called a user-defined exception or customized exception.
The user can define custom exceptions by creating a new class. This new exception class has to derive either directly or indirectly from the built-in class Exception
. In Python, most of the built-in exceptions also derived from the Exception
class.
class Error(Exception):
"""Base class for other exceptions"""
pass
class ValueTooSmallError(Error):
"""Raised when the input value is small"""
pass
class ValueTooLargeError(Error):
"""Raised when the input value is large"""
pass
while(True):
try:
num = int(input("Enter any value in 10 to 50 range: "))
if num < 10:
raise ValueTooSmallError
elif num > 50:
raise ValueTooLargeError
break
except ValueTooSmallError:
print("Value is below range..try again")
except ValueTooLargeError:
print("value out of range...try again")
print("Great! value in correct range.")
Output
Enter any value in 10 to 50 range: 5
Value is below range..try again
Enter any value in 10 to 50 range: 60
value out of range...try again
Enter any value in 10 to 50 range: 11
Great! value in correct range.
In the above example, we create two custom classes or user-defined classes with names, ValueTooSmallError
and ValueTooLargeError
.When the entered value is below the range, it will raise ValueTooSmallError
and if the value is out of then, it will raise
ValueTooLargeError
.
Customizing Exception Classes
We can customize the classes by accepting arguments as per our requirements. Any custom exception class must be Extending from BaseException
class or subclass of BaseException
.
In the above example, we create a custom class that is inherited from the base class Exception
. This class takes one argument age. When entered age is negative, it will raise NegativeAgeError
.
class NegativeAgeError(Exception):
def __init__(self, age, ):
message = "Age should not be negative"
self.age = age
self.message = message
age = int(input("Enter age: "))
if age < 0:
raise NegativeAgeError(age)
# Output:
# raise NegativeAgeError(age)
# __main__.NegativeAgeError: -9
Output:
Enter age: -28 Traceback (most recent call last): File "E:/demos/exception.py", line 11, in raise NegativeAgeError(age) main.NegativeAgeError: -28
Done
Exception Lifecycle
- When an exception is raised, The runtime system attempts to find a handler for the exception by backtracking the ordered list of methods calls. This is known as the call stack.
- If a handler is found (i.e., if
except
block is located), there are two cases in theexcept
block; either exception is handled or possibly re-thrown. - If the handler is not found (the runtime backtracks to the method chain’s last calling method), the exception stack trace is printed to the standard error console, and the application stops its execution.
Example
def sum_of_list(numbers):
return sum(numbers)
def average(sum, n):
# ZeroDivisionError if list is empty
return sum / n
def final_data(data):
for item in data:
print("Average:", average(sum_of_list(item), len(item)))
list1 = [10, 20, 30, 40, 50]
list2 = [100, 200, 300, 400, 500]
# empty list
list3 = []
lists = [list1, list2, list3]
final_data(lists)
Output
Average: 30.0 Traceback (most recent call last): File "E:/demos/exceptions.py", line 17, in final_data(lists) File "E:/demos/exceptions.py", line 11, in final_data print("Average:", average(sum_of_list(item), len(item))) Average: 300.0 File "E:/demos/exceptions.py", line 6, in average return sum / n ZeroDivisionError: division by zero
The above stack trace shows the methods that are being called from main() until the method created an exception condition. It also shows line numbers.
Warnings
Several built-in exceptions represent warning categories. This categorization is helpful to be able to filter out groups of warnings.
The warning doesn’t stop the execution of a program it indicates the possible improvement
Below is the list of warning exception
Waring Class | Meaning |
---|---|
Warning | Base class for warning categories |
UserWarning | Base class for warnings generated by user code |
DeprecationWarning | Warnings about deprecated features |
PendingDeprecationWarning | Warnings about features that are obsolete and expected to be deprecated in the future, but are not deprecated at the moment. |
SyntaxWarning | Warnings about dubious syntax |
RuntimeWarning | Warnings about the dubious runtime behavior |
FutureWarning | Warnings about probable mistakes in module imports |
ImportWarning | Warnings about probable mistakes in module imports |
UnicodeWarning | Warnings related to Unicode data |
BytesWarning | Warnings related to bytes and bytearray. |
ResourceWarning | Warnings related to resource usage |