Read from the bottom
The last line names the exception and message. Move upward to the first frame in your own code, then inspect the values used on that line.
Reduce the input
Create the smallest record, request, or file that still fails. A minimal reproduction removes unrelated behavior and makes the cause easier to see.
Check assumptions
Print or inspect the type, representation, and boundary values. Many bugs are mismatches between what the programmer assumed and what the data actually contains.
Turn the fix into a test
A regression test proves the failure stays fixed. It also documents the edge case for the next person.
Working example
def average(values):
if not values:
raise ValueError("values must not be empty")
return sum(values) / len(values)
def test_average_rejects_empty_list():
try:
average([])
except ValueError as exc:
assert str(exc) == "values must not be empty"
else:
raise AssertionError("expected ValueError")
test_average_rejects_empty_list()