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.
Before this lesson: Python Booleans, Python Exceptions: try, except, else and finally
2AssertionError: diagram
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.
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
- The last line shows the message you wrote. This is why a descriptive message that includes the actual values saves minutes of debugging.
- The line above shows which
assertfailed. 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. - 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.
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.
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?.
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.
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.
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.
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.
- Line 3-5
averageasserts that the list is not empty before dividing. The message says what is wrong in plain words. - Line 7-11
runwraps the call in try/except so we can see the AssertionError message without the program stopping. - Line 13A normal list passes the assert and the average of 80, 90 and 100 is 90.0.
- Line 14An empty list fails the assert, so the message is printed instead of a ZeroDivisionError appearing later.
- Line 16-17
0.1 + 0.2is slightly more than 0.3 in binary floating point, so==gives False whilemath.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")✓ 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"✓ 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)✓ 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)✓ 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 ✓ CompletedRead the code and type exactly what it prints, then press Check.
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)err prints.ok 5
ok 50
failed: huge value 500
2. Make the guard work
Fix the bug Easy ✓ Completedwithdraw(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.
def withdraw(balance, amount):
assert (amount <= balance, "Insufficient funds")
return balance - amount
print(withdraw(100, 30))def withdraw(balance, amount):
assert amount <= balance, "Insufficient funds"
return balance - amount
print(withdraw(100, 30))
2 more exercises → 5 more examples
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