{"id":"code-dev-py-fix-keyerror-one-function","audience":"developers","category":"coding","track":"coding","technique":"constraints","title":"Fix a KeyError from a stack trace without touching anything else","free":false,"task":"The nightly report crashed with the stack trace below after the store switched to a new CSV export. Your lead decided two things: a missing or blank discount counts as 0, and rows with status 'refunded' must be skipped entirely. Team rule for hotfixes: every changed or added line ends with the comment # ORD-412. Return only the fixed summarize_orders function in exactly 1 code block of at most 16 lines, with the same name, argument and return keys, no new imports, and under 25 words of explanation.","materialLabel":"The script, the new CSV and the stack trace (attached to your prompt automatically)","material":"# orders/report.py  (Python 3.8, runs nightly on the reports box)\nimport csv\nfrom datetime import date\n\nTAX_RATE = 0.08\n\ndef load_orders(path):\n    with open(path, newline=\"\") as f:\n        return list(csv.DictReader(f))\n\ndef summarize_orders(orders):\n    total = 0.0\n    count = 0\n    for order in orders:\n        price = float(order[\"price\"])\n        qty = int(order[\"qty\"])\n        discount = float(order[\"discount\"])\n        total += price * qty - discount\n        count += 1\n    return {\"total\": round(total * (1 + TAX_RATE), 2), \"count\": count}\n\ndef write_report(summary, out_path):\n    with open(out_path, \"w\") as f:\n        f.write(f\"{date.today()}: {summary['count']} orders, ${summary['total']}\\n\")\n\nif __name__ == \"__main__\":\n    write_report(summarize_orders(load_orders(\"orders.csv\")), \"report.txt\")\n\n--- first lines of the new orders.csv from the Brightcart export ---\norder_id,price,qty,status,discount\n1001,19.99,2,paid,\n1002,5.00,1,refunded,1.00\n1003,12.50,4,paid,2.50\n\n--- stack trace ---\nTraceback (most recent call last):\n  File \"orders/report.py\", line 27, in <module>\n    write_report(summarize_orders(load_orders(\"orders.csv\")), \"report.txt\")\n  File \"orders/report.py\", line 17, in summarize_orders\n    discount = float(order[\"discount\"])\nKeyError: 'discount'\n\n(Some rows from the older export have no discount column at all, which is\nwhere the KeyError comes from.)","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":16},{"type":"maxProseWords","max":25}],"mistakes":[{"title":"Getting the whole file back","why":"Ask the AI to fix an error and it often reprints the entire script, so you have to diff it yourself to find what changed.","fix":"Say: \"Return only the fixed summarize_orders function, nothing else.\"","checks":["maxCodeLines","codeBlocks"]},{"title":"Missing the blank-value case","why":"The usual fix, .get(\"discount\", 0), stops the KeyError but still crashes on the blank discount in row 1001.","fix":"Add: \"Treat a missing or blank discount as 0, e.g. float(order.get('discount') or 0).\"","checks":["r0"]},{"title":"Not passing on the team's decision","why":"The AI can't know refunded rows should be skipped, so it keeps counting them.","fix":"Add: \"Skip rows where status is 'refunded'.\"","checks":["r1"]},{"title":"Skipping the team's hotfix tag","why":"The AI doesn't know your team tags hotfix lines, and it likes to rename things or add its own comments.","fix":"Add: \"End each changed or added line with # ORD-412. Keep the name, argument and return keys.\"","checks":["r2"]},{"title":"Getting an essay","why":"By default the AI explains the bug at length before and after the code.","fix":"Add: \"No explanation, just the code block.\"","checks":["maxProseWords"]}],"promptHints":[{"label":"Asks for only the one function","patterns":["only.{0,30}(function|summarize_orders)","just the (function|fixed)","not the whole file"]},{"label":"Covers blank as well as missing","patterns":["blank","empty","or 0"]},{"label":"Says to skip refunded rows","patterns":["refund"]},{"label":"Gives the hotfix tag","patterns":["ORD-412","tag","comment"]},{"label":"Limits the explanation","patterns":["no explanation","no prose","only the code","under \\d+ words"]}],"hint":"The AI doesn't know your lead's two decisions or that you only want a small patch. Tell it what counts as 0, what to skip, how your team tags hotfix lines, and exactly what to hand back.","rubricLabels":["Treats a missing OR blank discount as 0 so neither crashes (e.g. float(order.get(\"discount\") or 0)); a plain order.get(\"discount\", 0) passed to float() fails because a blank string would still crash","Skips rows whose status is 'refunded' so they add nothing to the total or the count","Every changed or added line ends with the comment # ORD-412, unchanged lines have no new comments, and def summarize_orders(orders): and the return keys total and count stay the same"]}