An assert statement is a checkpoint the programmer placed deliberately: “this must be true here; if it is not, stop.” AssertionError is the stop. When it carries no message, the traceback line itself is the information, pointing at the exact assumption that failed. In test suites the same exception is how frameworks like pytest report a failed comparison, dressed up with the values involved.
def withdraw(balance, amount):
assert amount <= balance, "overdraft"
return balance - amount
withdraw(50, 80)
Traceback (most recent call last):
File "main.py", line 5, in <module>
AssertionError: overdraft
-O flag strips asserts entirely, so validation written as assert silently vanishes under optimization, and user input is exactly what asserts are not for. Raise real exceptions (ValueError, TypeError) for checks that must always run.assert x > 0 failing tells you where; assert x > 0, f"x was {x}" tells you why. Future debugging time is bought here.An assert that fires only under concurrency or after long uptime marks a state that degrades: resources exhausted, caches stale, counters wrapped. Reproduce by logging the asserted values with timestamps over time rather than rerunning once, since a single fresh run will pass and teach nothing.
Why does my AssertionError have no message at all? Because the assert was written without one: assert condition raises a bare AssertionError. The useful evidence is the traceback itself, the line and the code it shows. Adding a message (or the values) at the assert is the improvement to make while you are there.
Can I catch AssertionError and recover? You can, but for your own asserts it defeats the purpose: they exist to halt on states the program does not know how to handle. Catch it at a boundary if you must (to log and exit cleanly), not to continue past a broken assumption.