{"course":{"slug":"python-for-ai","title":"Python, from Zero, for AI","url":"https://addaly.com/learn/python-for-ai","level":"beginner","track":"build"},"lesson":{"slug":"reading-errors","title":"Reading an error message properly","url":"https://addaly.com/learn/python-for-ai/reading-errors","position":30,"of":89,"minutes":9,"answer":"Read a traceback bottom line first for what broke, then the lowest block of your own code for where.","keyIdea":"Read a traceback bottom line first for what broke, then the lowest block of your own code for where."},"body":{"format":"markdown","text":"## Errors are the most useful output Python produces\n\nNobody writes correct code first time. Not beginners, not people with twenty years of practice. The difference between the two is almost entirely how fast they read the error and know where to look. This lesson is that skill, and it is worth more than any syntax you will learn this month.\n\nThere are two kinds of failure, and telling them apart saves you time.\n\n## Kind one: it never started\n\n```python\nprices = [120, 340, 90\nprint(\"done\")\n```\n\n```\n  File \"budget.py\", line 1\n    prices = [120, 340, 90\n             ^\nSyntaxError: '[' was never closed\n```\n\nA `SyntaxError` means Python could not read the file, so nothing ran at all. No output from earlier lines, because there were no earlier lines as far as Python is concerned.\n\nOne honest warning: for unclosed brackets and quotes, the reported line is often not where you would say the mistake is. Python keeps reading, hoping the bracket closes, and complains where it gives up. Newer Python versions point back at the opening bracket, as above. Older ones say `invalid syntax` on the *following* line. So when a syntax error makes no sense, look at the line above it, and count your brackets and quotes.\n\n## Kind two: it started and then hit something impossible\n\nThis is a **traceback**, and it has a shape worth learning. Here is a real program:\n\n```python\n# budget.py\nbills = {\"food\": [200, 150], \"transport\": [60]}\n\ndef total_for(name):\n    return sum(bills[name])\n\nprint(total_for(\"food\"))\nprint(total_for(\"rent\"))\n```\n\nRunning it:\n\n```\n350\nTraceback (most recent call last):\n  File \"budget.py\", line 8, in <module>\n    print(total_for(\"rent\"))\n          ~~~~~~~~~^^^^^^^^\n  File \"budget.py\", line 5, in total_for\n    return sum(bills[name])\n               ~~~~~^^^^^^\nKeyError: 'rent'\n```\n\n**Read the last line first.** `KeyError: 'rent'` is what went wrong: something asked a dict for a key called `rent` and there is no such key. The error type tells you the category; the bit after the colon tells you the specific value involved.\n\n> Figure: Reading a traceback in the order that finds the bug. Steps: 1. Last line first (The error type and message: what went wrong); 2. Lowest block naming your file (That is where it broke); 3. Then read upwards (Oldest call at the top, most recent at the bottom); 4. Print the values (The line usually looks right; the value handed to it was not); 5. Fix the caller (The line that raised is often correct). Reading top to bottom is the habit to break: the top of a traceback is the oldest call, which is usually main() and tells you nothing. Everything printed before the traceback did happen, so the program worked until it did not.\n\n**Then read upwards.** The blocks above are the chain of calls that got you there, oldest at the top, most recent at the bottom, which is what \"most recent call last\" means in the header. The bottom block, line 5, is where the failure actually happened. The block above it, line 8, is the line that called it.\n\nSo the broken line is 5, and the reason it broke is on line 8: someone asked for `\"rent\"`, which is not in `bills`. Fixing line 5 would be fixing the wrong thing. The squiggles and carets under the code point at the exact expression that failed, which is a real help on a long line.\n\nAlso notice: `350` printed first. The program worked until it did not. Whatever printed before the traceback did happen.\n\n## The errors you will actually meet\n\n- `NameError: name 'totl' is not defined`: a typo, or you used a name before creating it, or it was created inside a function.\n- `TypeError: can only concatenate str (not \"int\") to str`: a number where text was expected, or the reverse. Very often an `input()` you forgot to convert.\n- `TypeError: ... 'NoneType' ...`: you used the result of a function that printed instead of returning.\n- `IndexError: list index out of range`: you asked for position 3 of a 3-item list. Valid positions are 0, 1, 2.\n- `KeyError: 'rent'`: the dict has no such key. Check spelling and capitals.\n- `AttributeError: 'list' object has no attribute 'split'`: you called a string method on a list, or similar. The message names the type you actually had, which usually tells you what went wrong earlier.\n- `ModuleNotFoundError: No module named 'requests'`: not installed, or installed into a different Python. That is lesson nine.\n- `ZeroDivisionError`: you divided by a count that turned out to be zero.\n- `IndentationError` / `TabError`: spacing is inconsistent. Pick four spaces and never mix in tabs.\n\n## How to work through one\n\n1. Read the bottom line. That is what happened.\n2. Find the lowest block naming *your* file. That is where.\n3. Look at the values, not the code. Print them just above the failing line: `print(repr(name), bills.keys())`. `repr` shows quotes and hidden spaces that `print` hides.\n4. Change one thing. Run again.\n\nWhen you ask another person, or an AI assistant, paste the **whole** traceback, not just the last line. The chain of calls is usually where the answer is, and the last line alone is often unanswerable.\n\n## Try this now\n\nRun the `budget.py` above exactly as written. Then change `\"rent\"` to `\"transport\"` and run again. Then delete the `return` from `total_for` and run again, and read the new error type carefully."},"check":{"question":"Given the traceback above, a student says: \"Line 8 is the broken line, so I will rewrite line 8.\" What is the better reading?","options":[{"text":"Both lines are faulty, because a traceback lists every line in the file that contains a mistake.","correct":false,"tempting":"A traceback really does list several lines of code, so it looks like a report of multiple independent problems to work through."},{"text":"The failure is inside Python's built-in `sum`, since that is the last function named before the error.","correct":false,"tempting":"`sum` is the last thing you see before the error line, and blaming the unfamiliar built-in feels safer than blaming your own dict."},{"text":"Line 8 is where the error is, because Python lists the location of the mistake at the top of the traceback.","correct":false,"tempting":"Reading top to bottom is the habit of a lifetime, and the first file-and-line you see naturally reads like the headline of the report."},{"text":"The failure happened at line 5; line 8 is only the call that led there, and the real problem is that `bills` has no key `\"rent\"`.","correct":true}]},"prev":{"slug":"command-line-arguments","title":"Giving your script a proper command line","url":"https://addaly.com/learn/python-for-ai/command-line-arguments"},"next":{"slug":"exceptions-and-try","title":"Catching an error, and deciding whether you should","url":"https://addaly.com/learn/python-for-ai/exceptions-and-try"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Reading an error message properly', in Python, from Zero, for AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/python-for-ai/reading-errors (Accessed: 31 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Reading an error message properly","courseTitle":"Python, from Zero, for AI","module":{"index":4,"slug":"errors-debugging-testing","title":"When it goes wrong: exceptions, debugging and tests"},"answer":null,"keyIdea":"Read a traceback bottom line first for what broke, then the lowest block of your own code for where.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure 4.1","figure":{"type":"flow","steps":[{"label":"Last line first","detail":"The error type and message: what went wrong"},{"label":"Lowest block naming your file","detail":"That is where it broke"},{"label":"Then read upwards","detail":"Oldest call at the top, most recent at the bottom"},{"label":"Print the values","detail":"The line usually looks right; the value handed to it was not"},{"label":"Fix the caller","detail":"The line that raised is often correct"}],"title":"Reading a traceback in the order that finds the bug","caption":"Reading top to bottom is the habit to break: the top of a traceback is the oldest call, which is usually main() and tells you nothing. Everything printed before the traceback did happen, so the program worked until it did not."},"alt":"Figure: Reading a traceback in the order that finds the bug. Steps: 1. Last line first (The error type and message: what went wrong); 2. Lowest block naming your file (That is where it broke); 3. Then read upwards (Oldest call at the top, most recent at the bottom); 4. Print the values (The line usually looks right; the value handed to it was not); 5. Fix the caller (The line that raised is often correct). Reading top to bottom is the habit to break: the top of a traceback is the oldest call, which is usually main() and tells you nothing. Everything printed before the traceback did happen, so the program worked until it did not."}},"blocks":[{"t":"h2","id":"errors-are-the-most-useful-output-python-produces","runs":[{"text":"Errors are the most useful output Python produces"}]},{"t":"p","runs":[{"text":"Nobody writes correct code first time. Not beginners, not people with twenty years of practice. The difference between the two is almost entirely how fast they read the error and know where to look. This lesson is that skill, and it is worth more than any syntax you will learn this month."}]},{"t":"p","runs":[{"text":"There are two kinds of failure, and telling them apart saves you time."}]},{"t":"h2","id":"kind-one-it-never-started","runs":[{"text":"Kind one: it never started"}]},{"t":"code","lang":"python","label":"Python","text":"prices = [120, 340, 90\nprint(\"done\")"},{"t":"code","lang":"","label":"Text","text":"  File \"budget.py\", line 1\n    prices = [120, 340, 90\n             ^\nSyntaxError: '[' was never closed"},{"t":"p","runs":[{"text":"A "},{"text":"SyntaxError","code":true},{"text":" means Python could not read the file, so nothing ran at all. No output from earlier lines, because there were no earlier lines as far as Python is concerned."}]},{"t":"p","runs":[{"text":"One honest warning: for unclosed brackets and quotes, the reported line is often not where you would say the mistake is. Python keeps reading, hoping the bracket closes, and complains where it gives up. Newer Python versions point back at the opening bracket, as above. Older ones say "},{"text":"invalid syntax","code":true},{"text":" on the "},{"text":"following","i":true},{"text":" line. So when a syntax error makes no sense, look at the line above it, and count your brackets and quotes."}]},{"t":"h2","id":"kind-two-it-started-and-then-hit-something-impossible","runs":[{"text":"Kind two: it started and then hit something impossible"}]},{"t":"p","runs":[{"text":"This is a "},{"text":"traceback","b":true},{"text":", and it has a shape worth learning. Here is a real program:"}]},{"t":"code","lang":"python","label":"Python","text":"# budget.py\nbills = {\"food\": [200, 150], \"transport\": [60]}\n\ndef total_for(name):\n    return sum(bills[name])\n\nprint(total_for(\"food\"))\nprint(total_for(\"rent\"))"},{"t":"p","runs":[{"text":"Running it:"}]},{"t":"code","lang":"","label":"Text","text":"350\nTraceback (most recent call last):\n  File \"budget.py\", line 8, in <module>\n    print(total_for(\"rent\"))\n          ~~~~~~~~~^^^^^^^^\n  File \"budget.py\", line 5, in total_for\n    return sum(bills[name])\n               ~~~~~^^^^^^\nKeyError: 'rent'"},{"t":"p","runs":[{"text":"Read the last line first.","b":true},{"text":" "},{"text":"KeyError: 'rent'","code":true},{"text":" is what went wrong: something asked a dict for a key called "},{"text":"rent","code":true},{"text":" and there is no such key. The error type tells you the category; the bit after the colon tells you the specific value involved."}]},{"t":"p","runs":[{"text":"Then read upwards.","b":true},{"text":" The blocks above are the chain of calls that got you there, oldest at the top, most recent at the bottom, which is what “most recent call last” means in the header. The bottom block, line 5, is where the failure actually happened. The block above it, line 8, is the line that called it."}]},{"t":"p","runs":[{"text":"So the broken line is 5, and the reason it broke is on line 8: someone asked for "},{"text":"\"rent\"","code":true},{"text":", which is not in "},{"text":"bills","code":true},{"text":". Fixing line 5 would be fixing the wrong thing. The squiggles and carets under the code point at the exact expression that failed, which is a real help on a long line."}]},{"t":"p","runs":[{"text":"Also notice: "},{"text":"350","code":true},{"text":" printed first. The program worked until it did not. Whatever printed before the traceback did happen."}]},{"t":"h2","id":"the-errors-you-will-actually-meet","runs":[{"text":"The errors you will actually meet"}]},{"t":"ul","items":[[{"text":"NameError: name 'totl' is not defined","code":true},{"text":": a typo, or you used a name before creating it, or it was created inside a function."}],[{"text":"TypeError: can only concatenate str (not \"int\") to str","code":true},{"text":": a number where text was expected, or the reverse. Very often an "},{"text":"input()","code":true},{"text":" you forgot to convert."}],[{"text":"TypeError: ... 'NoneType' ...","code":true},{"text":": you used the result of a function that printed instead of returning."}],[{"text":"IndexError: list index out of range","code":true},{"text":": you asked for position 3 of a 3-item list. Valid positions are 0, 1, 2."}],[{"text":"KeyError: 'rent'","code":true},{"text":": the dict has no such key. Check spelling and capitals."}],[{"text":"AttributeError: 'list' object has no attribute 'split'","code":true},{"text":": you called a string method on a list, or similar. The message names the type you actually had, which usually tells you what went wrong earlier."}],[{"text":"ModuleNotFoundError: No module named 'requests'","code":true},{"text":": not installed, or installed into a different Python. That is lesson nine."}],[{"text":"ZeroDivisionError","code":true},{"text":": you divided by a count that turned out to be zero."}],[{"text":"IndentationError","code":true},{"text":" / "},{"text":"TabError","code":true},{"text":": spacing is inconsistent. Pick four spaces and never mix in tabs."}]]},{"t":"h2","id":"how-to-work-through-one","runs":[{"text":"How to work through one"}]},{"t":"ol","start":1,"items":[[{"text":"Read the bottom line. That is what happened."}],[{"text":"Find the lowest block naming "},{"text":"your","i":true},{"text":" file. That is where."}],[{"text":"Look at the values, not the code. Print them just above the failing line: "},{"text":"print(repr(name), bills.keys())","code":true},{"text":". "},{"text":"repr","code":true},{"text":" shows quotes and hidden spaces that "},{"text":"print","code":true},{"text":" hides."}],[{"text":"Change one thing. Run again."}]]},{"t":"p","runs":[{"text":"When you ask another person, or an AI assistant, paste the "},{"text":"whole","b":true},{"text":" traceback, not just the last line. The chain of calls is usually where the answer is, and the last line alone is often unanswerable."}]},{"t":"h2","id":"try-this-now","runs":[{"text":"Try this now"}],"role":"try"},{"t":"p","runs":[{"text":"Run the "},{"text":"budget.py","code":true},{"text":" above exactly as written. Then change "},{"text":"\"rent\"","code":true},{"text":" to "},{"text":"\"transport\"","code":true},{"text":" and run again. Then delete the "},{"text":"return","code":true},{"text":" from "},{"text":"total_for","code":true},{"text":" and run again, and read the new error type carefully."}]}],"check":{"question":[{"text":"Given the traceback above, a student says: “Line 8 is the broken line, so I will rewrite line 8.” What is the better reading?"}],"options":[{"text":[{"text":"Both lines are faulty, because a traceback lists every line in the file that contains a mistake."}],"correct":false,"tempting":[{"text":"A traceback really does list several lines of code, so it looks like a report of multiple independent problems to work through."}]},{"text":[{"text":"The failure is inside Python’s built-in "},{"text":"sum","code":true},{"text":", since that is the last function named before the error."}],"correct":false,"tempting":[{"text":"sum","code":true},{"text":" is the last thing you see before the error line, and blaming the unfamiliar built-in feels safer than blaming your own dict."}]},{"text":[{"text":"Line 8 is where the error is, because Python lists the location of the mistake at the top of the traceback."}],"correct":false,"tempting":[{"text":"Reading top to bottom is the habit of a lifetime, and the first file-and-line you see naturally reads like the headline of the report."}]},{"text":[{"text":"The failure happened at line 5; line 8 is only the call that led there, and the real problem is that "},{"text":"bills","code":true},{"text":" has no key "},{"text":"\"rent\"","code":true},{"text":"."}],"correct":true,"tempting":null}],"order":[2,0,3,1],"why":null},"prev":{"slug":"command-line-arguments","title":"Giving your script a proper command line","minutes":8},"next":{"slug":"exceptions-and-try","title":"Catching an error, and deciding whether you should","minutes":9}}}