In this article
A traceback shows where an operation failed, but the repair may belong where data entered the program. Follow a CSV quantity from text to arithmetic, then test the parsing contract before accepting an AI-generated fix.
Start with the input that reaches the failing frame
Suppose a small inventory import crashes while adding quantities. The assistant sees TypeError and suggests wrapping the addition in a try block. Before reviewing that repair, preserve the exact input and run a small reproduction. The goal is to understand which assumption failed, not merely to make the exception disappear.
The original illustrative program below reads two rows and should return five. It instead tries to add an integer accumulator to a string quantity. Python's default CSV reader produces strings rather than inferring numeric types, and DictReader exposes fields under their column names. Python CSV reader documentation That behavior is expected from the parser; the application has not yet established the type its arithmetic requires.
Save the code in a Python file and execute it. Observe that the failure occurs on the first quantity. Preserve the exception type, message, and application frames when preparing assistant context. A large unrelated log dump can obscure the small sequence that actually demonstrates the defect.
import csv
from io import StringIO
def total_quantity_broken(text):
total = 0
for row in csv.DictReader(StringIO(text)):
total += row["quantity"]
return total
assert total_quantity_broken("sku,quantity\na,2\nb,3\n") == 5
Turn the traceback into a falsifiable hypothesis
Python's exception tutorial explains that the final exception message describes the failure and preceding traceback frames show its execution context. Python errors and exceptions tutorial Read the final application frame, then work toward the code that produced the value. A line number identifies the failing operation; it does not by itself assign responsibility to that line.
Write a hypothesis that can be disproved: “The CSV boundary passes text to a function expecting integer quantities.” Inspect one parsed row and the type of its quantity. If it is already an integer, this hypothesis is wrong and you need a different explanation. If it is a string, check where conversion and validation were intended to happen.
Give the assistant the reproduction, the relevant frames, and the proposed contract. Ask it to explain the path from input to failure before suggesting a patch. A useful response should point to an actual value transition and name the observation that supports it. Confidence without that connection adds no evidence.
Reject a patch that turns bad rows into success
A tempting generated repair catches TypeError or ValueError and continues with the next row. Another replaces an invalid quantity with zero. Both may produce a number and remove the visible crash, yet neither follows from the original requirement. If a malformed row represents stock, silently discarding it changes what the reported total means.
Define the rule explicitly for this exercise: every row must contain a quantity that can be parsed as a nonnegative integer. Valid zero contributes zero. A blank, missing, fractional, negative, or nonnumeric quantity rejects the import with its logical record number. The function returns a total only when every row passes; it performs no external writes.
This policy is a design choice for the example, not a universal CSV convention. Some products permit partial imports, but they need an explicit rejected-row report and acceptance rules. Ask for that contract before accepting a patch that skips errors. A quiet exception handler cannot decide the product policy for you.
Parse once and preserve a useful failure cause
Place the conversion in a small boundary helper. Successful parsing produces an integer; failures raise a descriptive ValueError. For conversion failures, the from clause records the original exception as the explicit cause. Python documents this exception-chaining mechanism. Python errors and exceptions tutorial It lets a developer retain the underlying diagnostic while exposing a message tied to the import contract.
The record number below counts data records after the header, not physical lines. Quoted CSV fields may span lines, so avoid promising an exact file line based on enumerate. The example assumes a valid header containing quantity and does not implement a complete schema validator. Missing quantity values within a data row are still rejected.
The helper accepts integer text according to int, including surrounding whitespace and a leading plus sign. If the product requires a stricter textual format, add that requirement and tests. Do not describe this parser as enforcing a format it never checks. Its purpose is to establish the numeric value needed by the summation.
import csv
from io import StringIO
def parse_quantity(raw, record):
message = f"record {record}: quantity must be a nonnegative integer"
if not isinstance(raw, str):
raise ValueError(message)
try:
quantity = int(raw)
except ValueError as exc:
raise ValueError(message) from exc
if quantity < 0:
raise ValueError(message)
return quantity
def total_quantity(text):
rows = csv.DictReader(StringIO(text))
return sum(
parse_quantity(row.get("quantity"), record)
for record, row in enumerate(rows, start=1)
)
Verify correct totals and rejected inputs separately
Paste the corrected implementation above the following unittest checks. The first test uses literal totals for ordinary input, zero, and a header-only file. The second confirms that invalid records fail instead of returning a plausible partial result. A valid first row followed by a bad second row is especially useful: returning the first row's subtotal would hide the rejected data.
Check the message and chained cause for a nonnumeric value. That verifies the diagnostic behavior you actually promised, rather than merely asserting that some exception occurred. A malformed test setup could also throw, so an overly broad exception assertion may accept a failure unrelated to the import.
Python documents DictReader's treatment of rows with too few values: missing entries receive its restval, which defaults to None. Python CSV reader documentation Include that row shape in the integration test. This reaches the parser through the real CSV reader instead of testing only a manually constructed dictionary.
import unittest
class ImportTests(unittest.TestCase):
def test_totals(self):
self.assertEqual(total_quantity("sku,quantity\na,2\nb,3\n"), 5)
self.assertEqual(total_quantity("sku,quantity\na,0\n"), 0)
self.assertEqual(total_quantity("sku,quantity\n"), 0)
def test_bad_second_record(self):
for row in ("b,nope", "b,", "b,-1", "b,3.5", "b"):
with self.subTest(row=row):
with self.assertRaisesRegex(ValueError, "record 2: quantity"):
total_quantity(f"sku,quantity\na,2\n{row}\n")
def test_conversion_cause(self):
with self.assertRaises(ValueError) as caught:
total_quantity("sku,quantity\na,nope\n")
self.assertEqual(
str(caught.exception),
"record 1: quantity must be a nonnegative integer",
)
self.assertIsInstance(caught.exception.__cause__, ValueError)
if __name__ == "__main__":
unittest.main()
Check what changes when the example rejoins the application
The self-contained repair establishes conversion and rejection behavior for the stated inputs. It does not prove that a production import is atomic. If the real caller writes each row before parsing the next, a later error could leave earlier writes committed. Inspect that caller and choose validation-before-write or an appropriate transaction when the product requires all-or-nothing persistence.
Also check how the error reaches the user. An internal exception chain can aid debugging without exposing raw records or stack frames in a public response. Preserve the application's established error mapping, and keep the assistant's proposed logging changes within the task's scope.
Summarize the evidence concretely: the original example failed because quantity remained text; the corrected path produces integer totals and rejects the tested invalid rows. Report the commands actually run. Leave file encoding, header validation, huge inputs, and database transaction behavior as named boundaries until you have inspected and tested those parts.
Common questions about traceback-driven AI debugging
Should I patch the last line in the traceback? Sometimes. First identify the contract that the failing operation expected. Here the addition is reasonable after parsing; the missing conversion belongs at the input boundary. Another program may have a different owner for that responsibility.
Can I send only the exception message to the assistant? It may help generate hypotheses, but the message alone does not identify the data path. Supply a reproducible input, relevant frames, and the expected behavior when sharing that material is permitted.
Does catching an exception mean the bug is fixed? Only if the resulting behavior matches the agreed error policy. For this exercise, returning a partial number is incorrect. A useful repair makes valid input work and makes invalid input fail in the documented way.
Sources & editorial notes
This article was authored with AI using primary-source research and an automated technical review. It has not been reviewed by a human. Code examples are illustrative; run and verify them in your own environment.
- Python CSV reader documentation
Documents string-valued CSV fields, DictReader, and default values for missing row fields. Retrieved October 20, 2026; evergreen documentation, not a new announcement.
- Python errors and exceptions tutorial
Explains traceback context, exception messages, and explicit exception chaining with raise from. Retrieved October 20, 2026; evergreen documentation, not a new announcement.
Put the reading into practice
Work through an existing repository, review AI suggestions, and test the decisions behind your changes.
Explore practice problems