Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

When should a function raise an exception instead of returning an error value?

Quick answer

Return a value when an outcome is an ordinary possibility that callers are expected to handle as part of the task, such as finding no match. Consider raising an exception when the function cannot complete its promised operation and the caller should handle or propagate that failure separately. Make each outcome clear, avoid confusing errors with valid results, and follow the project’s conventions.

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I’m writing a Python function and I’m unsure whether a problem should be a return value or an exception. What’s the practical rule?

Geez

Ask what the caller is meant to do when this outcome occurs. If it is an ordinary possibility the caller should handle as part of the task, consider returning an explicit value. If the function cannot complete its promised operation, consider raising an exception so the caller can handle or propagate the failure separately.

For example, a search function might return None when there is no match, if absence is an expected result and cannot be confused with a valid match. By contrast, a function whose job is to load required content might raise if it cannot load that content.

In Python, raising an exception exits the current function and allows a handler farther up the call stack to catch it. [S2] That can help make a failure visible rather than treating it like an ordinary result. It does not mean exceptions are the right choice for every unsuccessful outcome.

A useful design question is: “Will callers commonly check for this outcome and choose what to do next?” If so, a return value may fit. Whatever you choose, document what the outcome means and what callers should do.

Follow-up question

Could you show the difference with a small example, including what the caller has to do?

Geez

Suppose a program looks up a name in a mapping. “No matching name” is an expected outcome, so the function can return a value that the caller checks:

def find_user(users, name):
    return users.get(name)

user = find_user(users, "Mina")
if user is None:
    print("No match")
else:
    print(user)

Now suppose a function is meant to load a required file and return its contents. Returning an empty string on failure could make it unclear whether loading succeeded. Letting the failure raise gives the caller an opportunity to respond:

def load_config(path):
    with open(path, encoding="utf-8") as file:
        return file.read()

try:
    config_text = load_config(path)
except OSError as error:
    print(f"Could not load configuration: {error}")

These examples depend on their assumptions. If a configuration file is optional, its absence might instead be a normal outcome that the function represents explicitly. The important point is to avoid making a failure indistinguishable from a valid result. Catch an exception where you can respond meaningfully; otherwise, handling it farther up may be more appropriate.

Follow-up question

What if the operation has several ordinary outcomes, or different kinds of failure? I don’t want callers to guess what a return value means.

Geez

Make the outcomes explicit in the function’s contract. If callers need to distinguish ordinary results, use clear named values or a documented result structure rather than ambiguous markers such as False, 0, or an empty string when those might also be valid results.

For example, a reservation check might have ordinary outcomes such as available and already_booked. A caller may need to respond differently to each, so a named status could be clearer than raising for one of them. If the check cannot be performed at all, decide whether the function should raise or return a separate failure result based on what callers need to do.

A C++/WinRT guideline recommends result or error codes for expected failures and exceptions for unexpected runtime errors. That is specific to C++/WinRT; it does not establish a Python rule. [S1] For your function, specify what each outcome means, whether it represents a usable result, and what action the caller should take.

Follow-up question

How do I settle the choice in a code review without turning it into a style argument?

Geez

Focus the review on the function’s contract and its callers. For each outcome, ask whether it is routine, what the caller should do, and whether the proposed representation could be mistaken for a valid result. Also check whether related functions use a compatible pattern, unless their contracts differ.

A review note can make the reasoning concrete:

Outcome: No matching record
Expected during normal use? Yes
Caller action: Show “not found” and continue
Choice: Return an explicit missing result

Outcome: Required file cannot be read
Expected during normal use? No, for this task
Caller action: Stop this operation and handle the failure
Choice: Raise an appropriate exception

These are example assumptions, not claims about your application. Replace them with the actual requirements and caller actions. Then document the choice so callers do not have to infer what a value or exception means.

A practical checklist

  • List successful results and expected alternatives, such as no match.

  • Specify what a normal caller should do for each outcome.

  • Choose an explicit return representation for routine alternatives callers need to distinguish.

  • Consider an exception when the promised operation cannot be completed and the caller should handle or propagate the failure.

  • Check that an error marker cannot be mistaken for a valid result, and document the contract.

A few more useful answers

Is returning `None` always a good way to indicate failure?

No. It can work when absence is a clear, expected outcome and None cannot be confused with a valid result. Otherwise, choose a clearer representation or an exception, and document the contract.

Should I catch every exception inside the function that raises it?

Not necessarily. Handle an exception where there is enough context to respond usefully. If the function cannot recover or add useful context, catching it may obscure the failure.

Does the C++/WinRT guidance decide what Python code should do?

No. It is specific to C++/WinRT. The Python source cited here describes how exceptions exit a function and can be caught farther up the call stack; it does not settle which signaling style to choose.

Sources

References used for this guide.

  1. [S1] Error handling with C++/WinRT — guidance on expected failures
    learn.microsoft.com · Checked
  2. [S2] The Working Programmer — Python: Flow Control
    learn.microsoft.com · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work