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.