An error raised deep inside a loop tells you what failed, not which record caused it. "invalid literal for int(): 'thirty'" in a 50,000-row file is a search job. Since Python 3.11, exc.add_note() attaches a line of context to the exception and lets it carry on up, and the traceback prints the note under the error.
Before
rows = [["Ada", "34"], ["Linus", "thirty"], ["Grace", "41"]] def load_ages(rows): ages = {} for name, age in rows: ages[name] = int(age) return ages try: load_ages(rows) except ValueError as exc: print(exc)
invalid literal for int() with base 10: 'thirty'
You know the value. You don't know the row, and on real data several rows might say "thirty".
After
Catch it where you still know the context, add a note, and re-raise with a bare raise so the original error and traceback survive.
rows = [["Ada", "34"], ["Linus", "thirty"], ["Grace", "41"]] def load_ages(rows): ages = {} for line_no, (name, age) in enumerate(rows, start=1): try: ages[name] = int(age) except ValueError as exc: exc.add_note(f"row {line_no}: name={name!r}") raise return ages try: load_ages(rows) except ValueError as exc: print(type(exc).__name__, "-", exc) print(exc.__notes__)
ValueError - invalid literal for int() with base 10: 'thirty' ["row 2: name='Linus'"]
Here the notes are printed by hand to show them. Left uncaught, Python prints the traceback with the note underneath the error line. Delete the outer try and run it to see.
Why it works
Notes are stored on the exception in a list, __notes__, and the traceback printer shows each one after the message. The exception keeps its type, so anything further up that catches ValueError still does. You can add several notes as the error passes through different layers: the row, then the file, then the job.
When not to use it
If you're going to handle the error right there, handle it, don't annotate it. And before 3.11 add_note doesn't exist; the older way is raise ValueError(f"row {line_no}: ...") from exc, which creates a new error and chains the original. That changes the message, so code matching on it may break. The exceptions lesson covers chaining in full.