Python Return Error From Function

Problem Formulation

πŸ’¬ Question: How do you write a function that returns a real Python error instead of an “error code” such as -1 or None in case something got wrong?

For example, you may want to check the function input arguments for having the correct type or length (in the case of iterable arguments) before running the function body. In case your test fails, you want to return an error message.

Here’s an example where you return -1 to indicate that there has been an error, i.e., the arguments have different lengths:

def f(l1, l2):
    if len(l1) != len(l2):
        # Error Should Be Returned
        return -1
    # Do stuff

However, returning a dummy value is not very pythonic.

Best Practice Solution

The best practice solution to indicate that there was an error in a Python function is not to return a dummy value but to raise an error using syntax such as raise ValueError("... your message ...").

The main advantage is that the error propagates (technical term: “bubbles up”) through the function call stack until it gets caught by an except statement.

Example: if function f() calls function g() which calls function h() which raises an error, the error will be propagated to g() and then to f() which may catch it using a try/except block.

Another advantage of raising an error is readability.

An error message points out the problem in an easy-to-understand English text message. A magic number is likely to be misinterpreted, e.g., the caller of the function may think that the magic number is the actual output of the function — and not an error code.

Example

Let’s have a look at a simplified example:

def f(l1, l2):
    if len(l1) != len(l2):
        raise ValueError("Arguments have different lengths!")
    # Do stuff


f('hello', 'hi')

This leads to the following output:

Traceback (most recent call last):
  File "C:\Users\...\code.py", line 8, in <module>
    f('hello', 'hi')
  File "C:\Users\...\code.py", line 4, in f
    raise ValueError("Arguments have different lengths!")
ValueError: Arguments have different lengths!

This leads us to the following question:

How to Catch the Error?

To catch the error and avoid that the error terminates your whole program, you need to catch it using a try/except block.

🌍 Recommended Tutorial: Python Try/Except — A Simple Guide

Here’s a minimal example:

def f(l1, l2):
    if len(l1) != len(l2):
        # Error Should Be Returned
        raise ValueError("Arguments have different lengths!")
    # Do stuff


try:
    f('hello', 'hi')
except:
    print('No error stack')

The output is simply:

No error stack

Note that the error will “bubble up” the function calls in case you have multiple functions calling each other until one function call is embedded in a try/except block and the error is caught by it.

Here’ we’ll never reach the highlighted line in the function g() because of the error propagating through the function stack:

def f(l1, l2):
    if len(l1) != len(l2):
        # Error Should Be Returned
        raise ValueError("Arguments have different lengths!")
    # Do stuff


def g(l1, l2):
    f(l1, l2)
    print('Never reached when error')


try:
    g('hello', 'hi')
except:
    print('No error stack')

Let’s have a look at a case where you don’t catch the error—how does the output “stack trace” look like?

def f(l1, l2):
    if len(l1) != len(l2):
        # Error Should Be Returned
        raise Error("Arguments have different lengths!")
    # Do stuff


def g(l1, l2):
    f(l1, l2)
    print('Never reached when error')


g([1, 2, 3], [])

Output:

Traceback (most recent call last):
  File "C:\...\code.py", line 13, in <module>
    g([1, 2, 3], [])
  File "C:\...\code.py", line 9, in g
    f(l1, l2)
  File "C:\...\code.py", line 4, in f
    raise Error("Arguments have different lengths!")
NameError: name 'Error' is not defined

You can see that the output gives you all details about the function calls and how the error propagates through the function stack.

Summary

To return an error from a Python function, don’t use dummy values such as return -1 or return None. Instead, use the raise keyword such as raise ValueError('your msg'). The error will “bubble up” the stack until caught by a try/except block.

Thanks for reading through the whole tutorial! ❀️ To keep learning, feel free to check out our free email academy and download our cheat sheets here: