AssertionError
Core Python Errors and Exceptions Error explained

Python AssertionError

1AssertionError: overview

An AssertionError means an assert statement checked a condition that turned out to be False, so something your code assumed to be true was not.

GoalBy the end you can write assert statements with useful messages, diagnose why one fails, avoid the tuple and float pitfalls, and know when to raise a real exception instead.

Before this lesson: Python Booleans, Python Exceptions: try, except, else and finally

2AssertionError: diagram

Python AssertionErrorassert total >= 0, "negative total"total >= 0 is TrueTruenothing happens, program continuesFalseelseraise AssertionError("negative total")next line runs only if the assert passed
An assert is a guard: when the condition is true it is invisible, and when it is false it stops the program with your message.

3AssertionError: explained step by step

An AssertionError is raised when an assert statement checks a condition and finds it is False. It is different from most errors: you wrote the check yourself, so the error means one of your own assumptions about the program was wrong. Assertions rely on boolean values and on the exception system described in Python exceptions.

What an assert statement does

assert condition, message is shorthand for "if not condition: raise AssertionError(message)". If the condition is true, nothing happens and the program continues. The message is optional but you should always write one, because a bare AssertionError gives you nothing to go on.

Python
stock = 3

try:
    assert stock > 5
except AssertionError as err:
    print("no message:", repr(str(err)))

try:
    assert stock > 5, f"need more than 5 in stock, found {stock}"
except AssertionError as err:
    print("AssertionError:", err)

assert stock > 0, "stock must be positive"   # passes silently
print("still running")

How to read the traceback

Traceback (most recent call last):
  File "main.py", line 12, in <module>
    assert total == expected, f"total {total} != {expected}"
AssertionError: total 59.97 != 60
  1. The last line shows the message you wrote. This is why a descriptive message that includes the actual values saves minutes of debugging.
  2. The line above shows which assert failed. There is no deeper cause to hunt for: the data reaching that line broke your assumption, so look at the code that produced the values.
  3. If there is no message, put the values in an f-string and run again.

Cause 1: a real bug your assert caught

The assertion is doing its job when a calculation produces something impossible. Here a discount function is expected never to return a negative price.

Python
def apply_discount(price, percent):
    result = price - price * percent / 100
    assert result >= 0, f"negative price {result}"
    return result

print(apply_discount(80, 25))
try:
    apply_discount(80, 150)
except AssertionError as err:
    print("AssertionError:", err)

The fix is not to delete the assert but to find out why percent could be 150 and handle that at the source.

Cause 2: comparing floats with ==

Decimal fractions such as 0.1 cannot be stored exactly in binary, so an exact == often fails. Compare with a tolerance using math.isclose(). This applies to any numbers that came from division or multiplication.

Python
import math

subtotal = 0.1 + 0.2
print(subtotal)
try:
    assert subtotal == 0.3, f"{subtotal} is not exactly 0.3"
except AssertionError as err:
    print("AssertionError:", err)

assert math.isclose(subtotal, 0.3)
print("isclose passed")

Cause 3: the tuple-in-parentheses trap

If you wrap the condition and message together in parentheses, you create a tuple. A non-empty tuple is always truthy, so the assertion can never fail. Python even warns SyntaxWarning: assertion is always true, perhaps remove parentheses?.

Python
check = (False, "this message is never shown")
print(bool(check))               # True: any non-empty tuple is truthy

assert (1 > 2, "math is broken")  # passes! it is a tuple
print("the broken assert did not stop us")

Write assert condition, "message" with a comma and no outer parentheses. To split a long assert over lines, put parentheses around the condition only, as in assert (a and b), "message". See truthy and falsy values for why a tuple counts as true.

Cause 4: an assertion used to validate user input

Python's -O flag removes every assert before running the program:

python -O script.py

If an assert was your only check that an age is positive, the check vanishes in that mode. So never use assert for data that comes from users, files or the network. Use an if and raise a proper exception such as ValueError instead.

Python
def set_age(age):
    if age < 0:
        raise ValueError(f"age cannot be negative, got {age}")
    return age

try:
    set_age(-4)
except ValueError as err:
    print("ValueError:", err)

A good rule: assert is for "this can't happen unless my code is wrong"; exceptions are for "the outside world gave me bad input".

Asserts in tests

Assertions are the heart of automated tests, where a failing assert marks a test as failed. Tools such as pytest read the assert and show both sides of the comparison for you. Learn the basics in Python testing. In tests it is fine, and expected, that asserts are everywhere.

Python
def is_valid_username(name):
    return 3 <= len(name) <= 12 and name.isalnum()

assert is_valid_username("asha99")
assert not is_valid_username("a")
assert isinstance(is_valid_username("kiran"), bool), "should return a bool"
print("all checks passed")

4AssertionError: syntax

assert condition
assert condition, "message explaining what went wrong"
assert isinstance(value, int), f"expected int, got {type(value)}"
assert math.isclose(a, b)      # float comparison

# Same as:
if not condition:
    raise AssertionError("message")

5AssertionError: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

Interactive example
import math

def average(scores):
    assert len(scores) > 0, "scores must not be empty"
    return sum(scores) / len(scores)

def run(scores):
    try:
        print("average:", average(scores))
    except AssertionError as err:
        print("AssertionError:", err)

run([80, 90, 100])
run([])

price = 0.1 + 0.2
print(price == 0.3, math.isclose(price, 0.3))
Expected outputaverage: 90.0
AssertionError: scores must not be empty
False True

7AssertionError: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 3-5average asserts that the list is not empty before dividing. The message says what is wrong in plain words.
  2. Line 7-11run wraps the call in try/except so we can see the AssertionError message without the program stopping.
  3. Line 13A normal list passes the assert and the average of 80, 90 and 100 is 90.0.
  4. Line 14An empty list fails the assert, so the message is printed instead of a ZeroDivisionError appearing later.
  5. Line 16-170.1 + 0.2 is slightly more than 0.3 in binary floating point, so == gives False while math.isclose() gives True.

8AssertionError: common mistakes

Putting the condition and message in parentheses

✗ Wrong

balance = 20
amount = 50
assert (amount <= balance, "Insufficient funds")
print("withdrawn")
SyntaxWarning: assertion is always true, perhaps remove parentheses?

✓ Right

balance = 20
amount = 50
assert amount <= balance, "Insufficient funds"
print("withdrawn")

The parentheses build a two-item tuple, which is always truthy, so this assert never fails and the withdrawal goes ahead. Remove the outer parentheses and use a comma.

Comparing floats with ==

✗ Wrong

total = 0.1 + 0.2
assert total == 0.3, "wrong total"
AssertionError: wrong total

✓ Right

import math
total = 0.1 + 0.2
assert math.isclose(total, 0.3), "wrong total"
print("ok")

Floating-point arithmetic is approximate. Use math.isclose(total, 0.3) or round both sides.

Using assert to validate user input

✗ Wrong

def set_quantity(qty):
    assert qty > 0, "quantity must be positive"
    return qty

set_quantity(-2)
AssertionError: quantity must be positive

✓ Right

def set_quantity(qty):
    if qty <= 0:
        raise ValueError("quantity must be positive")
    return qty

print(set_quantity(2))

Assertions disappear under python -O, so they cannot protect against bad input. Raise ValueError with an if check instead.

Leaving out the message

✗ Wrong

orders = [12, 7, -3]
assert all(o >= 0 for o in orders)
AssertionError

✓ Right

orders = [12, 7]
assert all(o >= 0 for o in orders), f"negative order in {orders}"
print("orders ok")

With no message you only see the bare class name. Say what you expected and print the offending values.

9AssertionError: exercises

Write your answer, then press Check. The checks run in your browser.

1. Predict which asserts fail

Predict the output Easy

Read the code and type exactly what it prints, then press Check.

Read this code
checks = [(5, "small"), (50, "large"), (500, "huge")]

for value, label in checks:
    try:
        assert value < 100, f"{label} value {value}"
        print("ok", value)
    except AssertionError as err:
        print("failed:", err)

2. Make the guard work

Fix the bug Easy

withdraw(balance, amount) should raise an AssertionError with the message "Insufficient funds" when amount is bigger than balance. At the moment the assert never fails. Fix the function.

Your code
def withdraw(balance, amount):
    assert (amount <= balance, "Insufficient funds")
    return balance - amount

print(withdraw(100, 30))

2 more exercises → 5 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

What is the difference between assert and raise?

assert checks a condition and raises AssertionError if it is false, and it can be removed entirely with python -O. raise always raises the exception you choose. Use assert for internal sanity checks and tests, and raise for errors you want to report to callers, such as bad input.

Why does my failing assert show no message?

You did not provide one. Write assert condition, "message" and include the actual values in an f-string. A message such as f"expected 3 items, got {len(items)}" makes the failure self-explanatory.

Should I use assert in production code?

Sparingly, and only for conditions that can never be false unless the program has a bug. Never use it for user input, file contents or network data, because the check disappears when Python runs in optimised mode.

Further reading

10Next lesson

Up nextPython StopIterationStopIteration is the exception an iterator raises from next() when it has no more values to give, which for loops norma…

Related:
Python BooleansPython Exceptions: try, except, else and finallyPython ValueErrorPython Testing

← Python RecursionError · All Errors and Exceptions lessons · Practise in the IDE