16 Errors and Exceptions
16.1 The Wall
AI wrapped everything in try: ... except: pass and your errors vanished. The program did not crash anymore, but it also did not work. It silently swallowed every problem. You had no idea where things went wrong because the error messages were being hidden.
Or the opposite: AI did not add error handling at all, and the program crashed on the first unexpected input with a traceback you could not read.
This chapter fixes that.
16.2 Thinking Session
16.2.1 Getting Oriented
What is the difference between a syntax error and an exception in Python? When I see a traceback, what information does it give me? Walk me through how to read a Python error message. I want to be able to look at AI-generated tracebacks and immediately find the problem.
Your AI should explain: syntax errors are caught before the code runs (missing colons, bad indentation), exceptions happen during execution (dividing by zero, missing keys, wrong types). The traceback reads bottom-up: the last line is the error, the lines above show the call stack. The file name and line number tell you exactly where to look.
16.2.2 Go Deeper
What are the most common Python exceptions? I see TypeError, ValueError, KeyError, IndexError, FileNotFoundError, and AttributeError in AI-generated code. For each one, tell me what causes it and show me a one-line example that triggers it.
TypeError: wrong type for an operation ("5" + 3). ValueError: right type but wrong value (int("hello")). KeyError: missing dictionary key (d["missing"]). IndexError: list index out of range ([1,2][5]). FileNotFoundError: file does not exist. AttributeError: calling a method that does not exist on that type.
Explain Python’s try/except properly. What is the right way to use it? Why is except: or except Exception: without specifying the error type considered bad practice? What is the finally clause for?
16.2.3 Challenge It
What is wrong with this error handling?
try:
data = json.load(open("config.json"))
result = data["setting"] / data["count"]
save_to_database(result)
except:
passThree problems: (1) bare except: catches everything including KeyboardInterrupt and SystemExit. (2) pass silently swallows errors. You will never know something went wrong. (3) the try block is too broad. It should be separate try/excepts for file reading, data access, and database saving. AI generates this pattern constantly.
16.2.4 What You Should Have Learned
- Syntax errors vs exceptions, different causes, different fixes
- How to read a traceback: bottom-up, file + line number
- The common exceptions: TypeError, ValueError, KeyError, IndexError
- try/except should catch specific exceptions, not bare except
- Never use
except: pass, at minimum, log the error finallyruns whether or not an exception occurred
16.3 The Gap
You can now read Python error messages and understand what went wrong. When AI generates error handling, you can evaluate whether it is catching the right exceptions or blindly swallowing everything. This is a critical skill, bad error handling is worse than no error handling.
In the Building Session, you will add proper error handling to your chatbot.
16.4 Building Session
16.4.1 The Spec
Add robust error handling to your chatbot:
- Handle
FileNotFoundErrorwhen loading conversation history - Handle
json.JSONDecodeErrorwhen the history file is corrupted - Handle
KeyboardInterrupt(Ctrl+C) gracefully - Log errors instead of silently ignoring them
16.4.2 Prompt It
Update my chatbot to v1.6. Add proper error handling:
- Wrap the history file loading in try/except that handles FileNotFoundError and json.JSONDecodeError separately, with specific error messages for each
- Add a try/except around the main loop that catches KeyboardInterrupt and saves history before exiting
- Add an error_log list that stores error messages during the session
- Add an “errors” command that shows any errors that occurred
- Never use bare except: or except: pass
Keep existing features.
16.4.3 Read the Code
Your AI will produce something like this:
def load_history():
"""Load history with specific error handling."""
try:
with open(HISTORY_FILE, "r") as f:
return json.load(f)
except FileNotFoundError:
print(f"{BOT_NAME}: No previous history found. "
"Starting fresh.")
return []
except json.JSONDecodeError:
print(f"{BOT_NAME}: History file corrupted. "
"Starting fresh.")
return []
def main():
"""Main loop with error handling."""
display_welcome(BOT_NAME, VERSION)
user_name = get_user_name(BOT_NAME)
history = load_history()
error_log = []
try:
while True:
user_input = input(f"{user_name}: ").strip()
if not user_input:
continue
# ... response logic ...
except KeyboardInterrupt:
print(f"\n{BOT_NAME}: Interrupted! Saving...")
finally:
save_history(history)
print(f"{BOT_NAME}: History saved. Goodbye!")Each except catches a specific exception with a helpful message. KeyboardInterrupt is caught separately. It is not a bug, it is a user action. The finally block saves history regardless of how the loop ends (quit command, Ctrl+C, or unexpected error). Errors are logged to a list, not silently discarded.
16.4.4 Stretch It
Add a custom exception class ChatbotError that is raised when the chatbot encounters an invalid command format. Catch it in the main loop and add the error to the error_log.
16.5 Your Chatbot So Far
- Ch 1-14: Full features with dictionaries and string processing
- Ch 15: File persistence
- Ch 16: Proper error handling, error logging, graceful shutdown
16.6 Quick Reference
# Specific exception handling
try:
result = int(user_input)
except ValueError:
print("Not a valid number")
except TypeError:
print("Wrong type")
# Multiple exceptions
except (ValueError, TypeError) as e:
print(f"Error: {e}")
# finally always runs
try:
f = open("file.txt")
except FileNotFoundError:
print("Missing file")
finally:
print("This always runs")
# Raise your own
raise ValueError("Invalid input")
# Custom exception
class ChatbotError(Exception):
pass