Python AssertionError and assert not working with -O (tested)
AssertionError means an assert condition was false. The bigger trap is the opposite: asserts that never fire. We measured both on Python 3.13.5.
1. What AssertionError means
def withdraw(balance, amount):
assert amount <= balance, f"insufficient funds: {amount} > {balance}"
return balance - amount
print(withdraw(100, 30))
print(withdraw(100, 500))
Output:
70
Traceback (most recent call last):
File "/tmp/as/b1.py", line 6, in <module>
print(withdraw(100, 500))
~~~~~~~~^^^^^^^^^^
File "/tmp/as/b1.py", line 2, in withdraw
assert amount <= balance, f"insufficient funds: {amount} > {balance}"
^^^^^^^^^^^^^^^^^
AssertionError: insufficient funds: 500 > 100
assert cond, msg raises AssertionError(msg) when cond is false. Measured on Python 3.13.5: the traceback points at the assert line and underlines the failing condition. Read the condition: the fix is in whatever made it false, not in the assert. A bare assert cond with no message gives an AssertionError with an empty message, so add one.
2. python -O deletes every assert
import subprocess, sys
src = "def withdraw(b, a):\n assert a <= b, 'insufficient funds'\n return b - a\nprint(withdraw(100, 500))\n"
for flags in ([], ["-O"]):
r = subprocess.run([sys.executable, *flags, "-c", src], capture_output=True, text=True)
print(flags or "default", "->", (r.stdout or r.stderr.strip().splitlines()[-1]).strip())
r = subprocess.run([sys.executable, "-c", "print(__debug__)"], capture_output=True, text=True, env={"PYTHONOPTIMIZE": "1"})
print("PYTHONOPTIMIZE=1 __debug__ ->", r.stdout.strip())
Output:
default -> AssertionError: insufficient funds
['-O'] -> -400
PYTHONOPTIMIZE=1 __debug__ -> False
Measured: the same call that raises by default returns -400 under python -O, a negative balance with no error. -O (and the PYTHONOPTIMIZE environment variable, which we measured setting __debug__ to False) strips assert statements at compile time. If anything in your deployment sets PYTHONOPTIMIZE (a Dockerfile ENV line, a service unit), an assert that guards real input can vanish in production.
3. assert with parentheses never fails
import warnings
warnings.simplefilter("always")
x = 0
# parentheses make a 2-tuple, which is always truthy: this never fails
exec(compile("assert (x > 1, 'x must be > 1')", "demo.py", "exec"))
print("passed?!")
Output:
demo.py:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
passed?!
Measured: assert (x > 1, 'msg') asserts a non-empty tuple, which is always true, so it passes with x = 0. Python only emits a SyntaxWarning at compile time, easy to miss. Write assert x > 1, 'msg' without the outer parentheses.
4. Validate input with raise, not assert
def withdraw(balance, amount):
if amount > balance:
raise ValueError(f"insufficient funds: {amount} > {balance}")
return balance - amount
try:
withdraw(100, 500)
except ValueError as e:
print("ValueError:", e)
Output:
ValueError: insufficient funds: 500 > 100
Use assert for internal invariants that should be impossible (and are fine to skip under -O). For user input, API data and anything that must always be checked, raise a specific exception such as ValueError or TypeError, which -O never removes.
Frequently asked questions
Why does my assert not raise AssertionError?
Either Python runs with -O / PYTHONOPTIMIZE (asserts are stripped; we measured a failing assert returning -400 instead), or the assert has parentheses around condition and message, making an always-true tuple.
How do I fix AssertionError in Python?
Read the condition the traceback underlines and fix whatever made it false. We measured the traceback pointing at the assert line on Python 3.13.5.
Should I use assert to validate input?
No. python -O removes asserts; we measured the check vanishing. Raise ValueError or TypeError for input validation.
Why is my AssertionError message empty?
A bare assert cond has no message. Add one: assert cond, 'what went wrong'.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: a failing assert and its traceback, the same code under python -O, PYTHONOPTIMIZE and __debug__, the parenthesised-tuple assert and its SyntaxWarning, and raise ValueError as the fix. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.