AssertionError

assert cond, msg is shorthand for "if not cond: raise AssertionError(msg)" — but only while __debug__ is true, so it must never guard real input.

InheritsBaseException›Exception›AssertionError
Runtime exceptionPython 3 (all)Live demo
AssertionError(*args)
Raised by
assert condition, message
Message
the message after the comma — empty if none
Quick fix
fix the broken assumption, or raise ValueError for bad input
Watch out
python -O strips every assert

Demo

Live evaluation
An assert with a message. The f-string is only evaluated when the check fails.
Try:
Inputs
ageinttry -5 or 200
Code
def set_age(age):
    assert 0 <= age <= 150, f'age out of range: {age}'
    return age
set_age(42)
Result
42

The message after the comma becomes str(e), which is what the traceback line shows. Everything here depends on asserts being enabled: run the same file with python -O and set_age(-5) returns -5 without complaint. That is why this pattern belongs in tests and internal sanity checks, while user-supplied ages need an if + raise ValueError.

Constructor

NameTypeRequiredDescription
*argsobjectnoassert passes its message expression as the single argument; with no message there are no args and the traceback line is bare AssertionError.

Attributes

AttributeTypeMeaning
argstupleThe assert message as a one-element tuple, or () when the assert had no message.

Common patterns

Internal invariant
State something that must be true if your own code is correct. If it fires, it is a bug in the program, not bad input.
def pop_min(heap):
    assert heap, 'pop_min() on empty heap — caller should check'
    return heap.pop(0)
Input validation done right
Data from users, files or the network must be checked with an if that survives -O.
def set_age(age):
    if not 0 <= age <= 150:
        raise ValueError(f'age out of range: {age}')
    return age
Tests
pytest rewrites plain assert statements to show both sides of a failed comparison.
def test_total():
    assert total([1, 2]) == 3

Examples

1. Assert without a message
x = -1 assert x > 0
Returns
AssertionError
2. Assert with a message
x = -1 assert x > 0, f'x must be positive, got {x}'
Returns
AssertionError: x must be positive, got -1
3. Passing assert does nothing
x = 5 assert x > 0 'ok'
Returns
'ok'
4. The message is e.args[0]
try: assert False, 'boom' except AssertionError as e: args = e.args args
Returns
('boom',)
5. Any object can be the message
try: assert 1 == 2, {'expected': 2} except AssertionError as e: detail = e.args[0] detail
Returns
{'expected': 2}
6. Raise it directly
raise AssertionError('unreachable branch')
Returns
AssertionError: unreachable branch
7. Asserts are on unless -O
__debug__
Returns
True

Pitfalls

1. assert (condition, message) never fails
Parentheses make a two-element tuple, and a non-empty tuple is always true. Python emits a SyntaxWarning for this, but the assert silently passes.
Tuple
x = -1
assert (x > 0, 'x must be positive')
'passed'
'passed'
No parentheses
x = -1
assert x > 0, 'x must be positive'
'passed'
AssertionError: x must be positive
2. Validating user input with assert
Under python -O (or PYTHONOPTIMIZE) the assert line is compiled away, so bad data flows straight through. compile(..., optimize=1) below is exactly what -O does to a module. Use an if and a real exception type.
assert under -O
src = '\n'.join([
    'def withdraw(balance, amount):',
    "    assert amount > 0, 'amount must be positive'",
    '    return balance - amount',
])
ns = {}
exec(compile(src, '<bank>', 'exec', optimize=1), ns)  # what python -O does
ns['withdraw'](100, -50)
150
if + ValueError
src = '\n'.join([
    'def withdraw(balance, amount):',
    '    if amount <= 0:',
    "        raise ValueError('amount must be positive')",
    '    return balance - amount',
])
ns = {}
exec(compile(src, '<bank>', 'exec', optimize=1), ns)  # what python -O does
ns['withdraw'](100, -50)
ValueError: amount must be positive
3. Side effects inside the assert
The whole expression disappears under -O — including a call that does real work. Do the work first, assert on the result.
Work inside assert
src = 'items = [3, 1]\nassert items.pop() == 1'
ns = {}
exec(compile(src, '<s>', 'exec', optimize=1), ns)  # what python -O does
ns['items']
[3, 1]
Work outside
src = 'items = [3, 1]\nlast = items.pop()\nassert last == 1'
ns = {}
exec(compile(src, '<s>', 'exec', optimize=1), ns)  # what python -O does
ns['items']
[3]

When to use

Use it
  • Internal invariants: "this can only happen if my code has a bug"
  • Test code (pytest, unittest) and debugging checks during development
  • Documenting an assumption right where it is relied on
Reach for something else
  • Validating arguments, user input, config or file contents → ValueError / TypeError
  • Security or permission checks — they vanish under -O
  • Expressions with side effects

Notes

Equivalent to
if __debug__: if not cond: raise AssertionError(msg)
python -O
__debug__ becomes False and assert statements are not compiled at all
pytest
rewrites assert in test files to report the compared values

FAQ

Put it after a comma: assert x > 0, f'x must be positive, got {x}'. The message expression is only evaluated when the assertion fails and becomes the exception's argument, so the traceback ends with AssertionError: x must be positive, got -1. Do not wrap condition and message in parentheses — that creates a tuple, which is always true.