runtime
89 / 250
lesson 89 Exceptions & Validation · intermediate

Raise errors with useful messages

A validation function that finds bad data should refuse it loudly: `raise ValueError("…")` stops the caller with a message that says exactly what was wrong.

You'll be able to
  • Raise ValueError(message) when a value breaks a rule, and catch it in the caller
  • Write messages that name the value and the rule: rating must be 1-5, got 7
  • Replace a library's vague error with your own clearer one
The idea

So far errors came from Python itself. Your own functions can raise them too. A function that checks a quantity has two choices when the quantity is wrong: return something odd (None, -1) and hope the caller checks, or raise an error the caller can't ignore. raise ValueError("quantity must be positive, got -4") stops the function on that line, exactly like int("twelve") does.

Raise ValueError when the type is right but the value is not acceptable. The message is for the person reading the report later: name the value and the rule.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Refuse a bad quantity
def checked_quantity(quantity):
    if quantity <= 0:
        raise ValueError(f"quantity must be positive, got {quantity}")
    return quantity

for q in [12, -4, 3]:
    try:
        print("ok", checked_quantity(q))
    except ValueError as err:
        print("refused:", err)
example-2.py · Nobody catches it
def checked_quantity(quantity):
    if quantity <= 0:
        raise ValueError(f"quantity must be positive, got {quantity}")
    return quantity

print(checked_quantity(5))
print(checked_quantity(0))

A function can also catch a library's error and raise a clearer one in its place. float("15 %") says *could not convert string to float*; a discount parser can say what it expected instead.

example-3.py · A discount code's percentage
def parse_discount(text):
    cleaned = text.strip().removesuffix("%")
    try:
        percent = float(cleaned)
    except ValueError:
        raise ValueError(f"discount must look like '15%', got {text!r}")
    if not 0 <= percent <= 100:
        raise ValueError(f"discount must be 0-100%, got {percent}")
    return percent / 100

for code in ["15%", " 7.5% ", "half", "120%"]:
    try:
        print(code, "->", parse_discount(code))
    except ValueError as err:
        print(code, "->", err)

When to use it: whenever a function receives a value it can't sensibly work with. Mistakes to avoid: returning a magic value (-1, "") that the caller forgets to check; a message such as "invalid" that says nothing; and raise ValueError without a message at all.

Review
  • raise ValueError("…") stops the function; the caller handles it like any other error.
  • A good message names the value and the rule it broke.
  • Catching a vague error and raising a clearer one is a normal pattern.
  • Next: your own error types, so callers can tell your problems apart.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Raise errors with useful messages — Exceptions & Validation · Runtime