Learn Python in 30 Days — Day 17: Error Handling & Debugging Mindset

Learn Python in 30 Days — Day 17: Error Handling & Debugging Mindset

Welcome to Day 17 of the Learn Python in 30 Days series!

Up to now, your code has mostly worked as long as the user behaves.
They type a number when you expect a number.
They don’t rename or delete files you’re trying to open.

Real life isn’t that kind.

Today you’ll learn how to:

  • Use try / except to catch and handle errors (exceptions)

  • Deal with two super-common problems:

    • ValueError — bad input / wrong data type

    • FileNotFoundError — missing files

  • Start thinking like a debugger: What could go wrong, and how will I deal with it?

All example files for this series are available on my GitHub: Learn-Python-in-30-Days

Step 1 – What Are Exceptions?

When Python hits a problem it can’t handle, it raises an exception and stops your program.

Example:

age = int(input("Enter your age: ")) print("Next year you’ll be", age + 1)

If the user types hello instead of a number:

Enter your age: hello Traceback (most recent call last): File "example.py", line 1, in <module> age = int(input("Enter your age: ")) ValueError: invalid literal for int() with base 10: 'hello'

Key parts to notice:

  • ValueError → the type of problem

  • The message after it → extra details

  • The traceback → where the error happened

Right now, Python just crashes.
Our job today is to catch these errors and respond more helpfully.

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Step 2 – Your First try/except

try / except lets you say:

“Try this code. If a specific error happens, run some backup code instead.”

The basic pattern for this code is as follows:

try: risky_code_here() except SomeErrorType: handle_the_problem_here()

Concrete example with int():

user_input = input("Enter a number: ") try: number = int(user_input) print("You entered:", number) except ValueError: print("That wasn’t a valid number.")

Now if the user types hello, your program doesn’t crash.
Instead, it prints a friendly message and continues.

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Step 3 – Handling ValueError (Bad Input)

Any time you convert user input (e.g. with int() or float()), there’s a risk of ValueError.

Let’s make a safe number input helper:

def get_int(prompt): """Ask the user for an integer, keep asking until they give a valid one.""" while True: text = input(prompt) try: value = int(text) return value except ValueError: print("Please enter a whole number (no letters or symbols).")

Use it like this:

age = get_int("Enter your age: ") print("Next year you’ll be", age + 1)

What’s happening:

  • The while True: loop keeps going

  • If int(text) works → we return value and exit the function

  • If it raises ValueError → we catch it and show a helpful message

This is defensive programming where you assume people will type nonsense and your code is ready for it.

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Step 4 – Handling FileNotFoundError (Missing Files)

Next common problem: working with files that don’t exist (yet).

file_name = "notes.txt" with open(file_name, "r") as f: content = f.read() print(content)

If notes.txt doesn’t exist, you’ll get:

FileNotFoundError: [Errno 2] No such file or directory: 'notes.txt'

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Let’s handle that error with the try/except routines: -

file_name = "notes.txt" try: with open(file_name, "r") as f: content = f.read() print("File contents:\n") print(content) except FileNotFoundError: print(f"File '{file_name}' not found.")

Now the program doesn’t crash, try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

But we can be even smarter: if the file doesn’t exist, ask if we should create it.

file_name = "notes.txt" try: with open(file_name, "r") as f: content = f.read() print("File contents:\n") print(content) except FileNotFoundError: print(f"File '{file_name}' not found.") create = input("Do you want to create it? (y/n): ").lower() if create == "y": with open(file_name, "w") as f: f.write("") # start with an empty file print(f"Created empty file '{file_name}'.") else: print("Okay, not creating the file.")

You’ve just made your code more user-friendly and robust.

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Step 5 – A Quick Look at else and finally

try / except has two optional friends: else and finally.

  • else → runs if no exception happened

  • finally → runs always, error or not (good for cleanup)

Example:

file_name = "data.txt" try: f = open(file_name, "r") except FileNotFoundError: print("No data file found.") else: print("File opened successfully.") print("First line:", f.readline()) f.close() finally: print("Done trying to read the file.")

For now, try + except is enough.
Just know these exist; we’ll lean on them more as programs get bigger.

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Step 6 – Debugging Mindset: Don’t Fear Errors

Today is also about how you think when something breaks.

Instead of:

“Ahh, it crashed! Python is broken!”

Try:

“Interesting. What exactly went wrong? Where? Why?”

When you see an error:

  1. Read the exception type

    • ValueError, FileNotFoundError, TypeError, etc.

  2. Read the message

    • It often tells you exactly what was wrong.

  3. Look at the last line of the traceback

    • That line is usually where the problem actually happened.

  4. Reproduce the error

    • Can you make it happen again with the same input? That’s good — now you can fix it.

  5. Add temporary print()s

    • Print variables right before the crash to see what they contain.

Example of simple “print debugging”:

def divide(a, b): print("DEBUG: a =", a, "b =", b) # temporary return a / b result = divide(10, 0)

You’d see b = 0 before getting ZeroDivisionError.
Now you know you need to handle zero differently.

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Step 7 – Common try/except Mistakes to Avoid

1. Catching everything with a bare except:

try: risky_stuff() except: pass # BAD: hides every problem

This is like putting tape over the “check engine” light in your car.

Better:

try: risky_stuff() except ValueError: print("There was a problem with the value.")

Or, if you really need to catch anything:

try: risky_stuff() except Exception as e: print("Something went wrong:", e)

…but don’t just silently ignore it.

2. Making the try block too big

Bad practice don't do this, keep it simple:

try: print("Starting...") x = int(input("Number: ")) y = 10 / x print("Result:", y) print("Finished.") except ValueError: print("Invalid number.")

If anything else goes wrong (like dividing by zero), it’ll still say “Invalid number.”

Better practice is to keep the risky line(s) tight:

print("Starting...") while True: text = input("Number: ") try: x = int(text) break except ValueError: print("Please enter a valid integer.") try: y = 10 / x print("Result:", y) except ZeroDivisionError: print("Cannot divide by zero.") print("Finished.")

Now each error is handled with the right message.

Step 8 – Mini-Project: Safer Temperature Converter (v2)

Let’s upgrade the Temperature Converter from Day 15 to be more robust.

Goal:

  • Ask the user to convert from Celsius to Fahrenheit

  • Use try/except to handle bad input

  • Log conversions to a file, handling FileNotFoundError if needed

def get_float(prompt): """Ask the user for a floating-point number, with ValueError handling.""" while True: text = input(prompt) try: value = float(text) return value except ValueError: print("Please enter a number (you can use decimals like 21.5).") def c_to_f(celsius): """Convert Celsius to Fahrenheit.""" return (celsius * 9 / 5) + 32 def log_conversion(celsius, fahrenheit, file_name="temp_log.txt"): """Append the conversion to a log file.""" try: with open(file_name, "a") as f: f.write(f"{celsius} C -> {fahrenheit} F\n") except FileNotFoundError: # This is rare for "a" mode, but we’ll be explicit anyway print(f"Could not find '{file_name}'. Creating new file.") with open(file_name, "w") as f: f.write(f"{celsius} C -> {fahrenheit} F\n") def main(): print("=== Temperature Converter (Celsius → Fahrenheit) ===") c = get_float("Enter temperature in °C: ") f = c_to_f(c) print(f"{c}°C is {f}°F") log_conversion(c, f) print("Conversion logged to temp_log.txt") if __name__ == "__main__": main()

What you’re practising here:

  • ValueError handling for user input

  • File writing with defensive thinking

  • Keeping logic in functions (Days 15–16)

Try it yourself, hopefully you'll see something like that shown below: -

You can also download this example from my GitHub here and run it yourself.

Next Up — Day 18 – Working with Files

Read/write text files. Mini-project: Simple Notes App.

All example files for this series are available on my GitHub: Learn-Python-in-30-Days

You can see the full series here Learn Python in 30 Days series!

Hope you have enjoyed this post, thanks Matty

Comments

Popular posts from this blog

Math Behind Logic Gates

6502 - Part 2 Reset and Clock Circuit

Building a 6502 NOP Test