Skip to Content
BonicBot A2DevelopmentPython ProgrammingLesson 14 - Error Handling (try-except)

Lesson 14 - Error Handling (try-except)

Learning Objective

Catch and handle specific exceptions with try / except, use else and finally, and raise your own exceptions when a check fails.


Introduction

Programs don’t always run perfectly.

Users enter unexpected values.

Files may be missing.

Network connections can fail.

Robots may become unreachable.

Without error handling, these situations cause a program to crash immediately.

Python provides exceptions and the try / except mechanism so programs can respond gracefully when something goes wrong.

Instead of crashing, your code can:

  • Display a helpful message
  • Retry an operation
  • Use a fallback value
  • Skip bad input
  • Clean up resources before exiting

In this lesson you’ll learn:

  • How exceptions work
  • How to catch specific errors
  • When to use else
  • When to use finally
  • How to raise your own exceptions

What Is an Exception?

An exception is an error that occurs while a program is running.

Example:

10 / 0

Produces:

ZeroDivisionError: division by zero

Another example:

int("abc")

Produces:

ValueError: invalid literal for int()

If these exceptions aren’t handled, the program stops immediately.


Catching Exceptions

The simplest pattern is:

try: risky_operation() except SomeException: handle_problem()

Example:

try: number = int("abc") except ValueError: print("That wasn't a valid number.")

Output:

That wasn't a valid number.

The program continues running instead of crashing.


Code

# error_handling.py def safe_divide(a, b): try: result = a / b except ZeroDivisionError: print("Cannot divide by zero.") return None else: print("Division succeeded.") return result finally: print("Division attempt finished.") print(safe_divide(10, 2)) print(safe_divide(10, 0)) # Catching a specific exception type across a loop values = ["12", "abc", "7"] for value in values: try: number = int(value) print(f"Converted: {number}") except ValueError: print(f"'{value}' is not a valid number.")

Expected Output

Click to see expected output

Division succeeded. Division attempt finished. 5.0 Cannot divide by zero. Division attempt finished. None Converted: 12 'abc' is not a valid number. Converted: 7

Understanding the Flow

Successful Case

safe_divide(10, 2)

Flow:

try else finally

Result:

Division succeeded. Division attempt finished. 5.0

Failure Case

safe_divide(10, 0)

Flow:

try except finally

Result:

Cannot divide by zero. Division attempt finished. None

Common Exception Types


Catching Specific Errors

Good practice:

try: number = int(text) except ValueError: print("Invalid number.")

Avoid:

try: number = int(text) except: print("Something went wrong.")

The second version hides all errors, including bugs you didn’t expect.

Specific exceptions make debugging much easier.


Using else

else runs only if the try block succeeds.

Example:

try: result = 10 / 2 except ZeroDivisionError: print("Bad division") else: print("Success!")

Output:

Success!

The advantage is that successful code stays separate from error-handling code.


Using finally

finally always executes.

Even if:

  • An exception occurs
  • The function returns early
  • The program leaves the try block

Example:

try: print("Working") finally: print("Cleanup")

Output:

Working Cleanup

This makes finally ideal for cleanup tasks.


Raising Your Own Exceptions

Sometimes Python doesn’t know something is wrong.

Example:

age = -5

A negative age is invalid, but Python doesn’t automatically raise an error.

You can raise one yourself:

if age < 0: raise ValueError("Age cannot be negative")

Output:

ValueError: Age cannot be negative

This allows your code to enforce rules and validate data.


🔧 Under the Hood

What’s the difference between else and finally?

Both appear after a try block, but they serve different purposes.

else

Runs only if the try block completed successfully.

Example:

try: result = 10 / 2 except ZeroDivisionError: print("Error") else: print("Success")

Output:

Success

If an exception occurs:

10 / 0

the else block is skipped entirely.


finally

Runs no matter what.

Example:

try: 10 / 0 except ZeroDivisionError: print("Error") finally: print("Cleanup")

Output:

Error Cleanup

Even if the function returns early:

try: return 42 finally: print("Cleanup")

the finally block still runs.


Why This Matters

Use:

else

for code that should execute only after success.

Use:

finally

for code that must execute regardless of success or failure.

Common examples:

  • Closing files
  • Closing network connections
  • Releasing hardware resources
  • Stopping robot motion safely

Why Avoid Bare except?

Bad:

except: pass

This catches:

  • ValueError
  • NameError
  • TypeError
  • Unexpected bugs

all at once.

Good:

except ValueError:

This catches only the error you’re prepared to handle.

Everything else remains visible during debugging.


Student Challenge

Challenge 1 — Python Only

Validate a list of ages while handling both invalid text and manually-raised errors.

  • Loop through entries ["25", "-4", "abc", "40"].
  • Convert each string to an integer with int(). If age < 0, manually raise a ValueError("Age cannot be negative").
  • Catch ValueError exceptions using except ValueError as exc: and print a skipping message.

Hint

Exceptions you raise yourself:

raise ValueError(...)

behave exactly like exceptions raised automatically by Python.

They can be caught by the same except ValueError: block.

Click to see solution

# safe_ages.py entries = ["25", "-4", "abc", "40"] for entry in entries: try: age = int(entry) if age < 0: raise ValueError("Age cannot be negative") print(f"Valid age: {age}") except ValueError as exc: print(f"Skipping '{entry}': {exc}")

Expected Output:

Valid age: 25 Skipping '-4': Age cannot be negative Skipping 'abc': invalid literal for int() with base 10: 'abc' Valid age: 40

Challenge 2 — Robot

Catch a real robot connection failure and check whether a servo command was rejected.

  • Wrap your BonicBot connection in a try block and catch BonicBotConnectionError (imported from bonicbot_bridge.exceptions).
  • Attempt to move the left arm to an out-of-bounds angle (shoulder=300, elbow=20) and check if move_left_arm returns False (indicating a rejected move).
  • Try changing the host address to an invalid address to confirm connection errors are caught gracefully.

Explore additional exception types in the Python SDK reference.

Hint

Python already includes a built-in exception named:

ConnectionError

This import:

ConnectionError as BonicBotConnectionError

avoids confusion between the built-in exception and the SDK-specific version.

Click to see solution

# challenge_safe_connect.py from bonicbot_bridge import BonicBot from bonicbot_bridge.exceptions import ( ConnectionError as BonicBotConnectionError ) try: with BonicBot( host='192.168.0.188', timeout=5 ) as bot: # 300° is outside the # left shoulder's (-45, 180) range success = bot.move_left_arm( shoulder=300, elbow=20 ) if not success: print( "Arm move rejected — " "angle was outside " "the servo's limits." ) bot.drive_distance( 0.3, speed=0.2 ) except BonicBotConnectionError as exc: print( f"Could not connect " f"to the robot: {exc}" )

Error Handling Best Practices

Catch Specific Exceptions

except ValueError:

instead of:

except:

Use Exceptions for Exceptional Situations

Good:

raise ValueError("Invalid age")

Bad:

raise Exception("Everything")

Use meaningful exception types whenever possible.


Clean Up with finally

finally: connection.close()

Cleanup code belongs here.


Don’t Hide Bugs

Avoid:

except: pass

This can make debugging extremely difficult.


Reflection Question

A bad connection raises a ConnectionError that you must catch with except, but an out-of-range servo angle simply returns False and prints a warning. Why might the SDK’s designers have chosen two different error-handling styles for these two situations?


By the end of this lesson, you should be comfortable catching exceptions, using try, except, else, and finally, raising your own exceptions, and deciding when an error should be handled versus when it should stop execution.

Last updated on