This document explains unhandled errors and exceptions in high-level languages like Python, covering error types, tracebacks, debugging techniques, logging strategies, and making programs resilient. Focus is on proper error handling and user-friendly failure modes.
This document explains how to handle unexpected errors and exceptions in high-level programming languages like Python, covering common error types, traceback analysis, debugging techniques including printf debugging and the logging module, and strategies for making programs resilient to failures.
While high-level languages like Python, Java, and Ruby handle memory management automatically, programs written in these languages can still encounter unexpected conditions that cause failures. When code doesn’t properly handle these conditions, it triggers errors or exceptions, causing programs to terminate unexpectedly.
High-level programming languages handle memory management automatically, eliminating many low-level memory errors. However, these languages can still trigger problems when encountering unexpected conditions not properly handled in the code.
| Error Type | Cause | Example |
|---|---|---|
| IndexError | Accessing list element beyond valid range | list[10] when list has 5 elements |
| TypeError | Operation on incompatible type | Adding string to integer |
| AttributeError | Accessing non-existent attribute | Calling method on uninitialized variable |
| ZeroDivisionError | Division by zero | x / 0 |
| KeyError | Accessing non-existent dictionary key | dict['missing_key'] |
| ValueError | Invalid value for operation | int('abc') |
Unhandled errors typically occur because code makes incorrect assumptions:
When unhandled errors occur, the interpreter provides diagnostic information:
The traceback displays the sequence of function calls leading to the error. This is crucial because:
Example traceback structure:
1Traceback (most recent call last):
2 File "main.py", line 15, in <module>
3 result = process_data(data)
4 File "main.py", line 8, in process_data
5 return data[10]
6IndexError: list index out of range
For Python programs, use the PDB (Python Debugger) interactive debugger:
1import pdb
2
3# Set breakpoint
4pdb.set_trace()
5
6# Or use Python 3.7+ built-in
7breakpoint()
| Command | Purpose |
|---|---|
n (next) | Execute next line |
s (step) | Step into function |
c (continue) | Continue execution |
p variable | Print variable value |
l (list) | Show current code context |
w (where) | Show stack trace |
Add print statements to display execution data:
1def process_data(items):
2 print(f"Processing {len(items)} items") # Debug output
3 for item in items:
4 print(f"Current item: {item}") # Debug output
5 result = transform(item)
6 print(f"Result: {result}") # Debug output
7 return results
Printf debugging involves inserting statements that display:
The name originates from C’s printf function, but applies to any language using print, puts, echo, or similar output functions.
The logging module provides configurable debug output that can be easily enabled or disabled:
1import logging
2
3# Configure logging level
4logging.basicConfig(level=logging.DEBUG)
5
6# Different message levels
7logging.debug("Detailed information for diagnosing problems")
8logging.info("General informational messages")
9logging.warning("Warning messages")
10logging.error("Error messages")
11logging.critical("Critical problems")
| Level | Value | Usage |
|---|---|---|
| DEBUG | 10 | Detailed diagnostic information |
| INFO | 20 | General informational messages |
| WARNING | 30 | Warning messages for potential issues |
| ERROR | 40 | Error messages for serious problems |
| CRITICAL | 50 | Critical problems causing program failure |
Logging level can be changed via:
This allows debug messages during development without cluttering production output.
Common fixes include:
Use try-except blocks to catch and handle errors gracefully:
1try:
2 with open('config.txt', 'r') as f:
3 config = f.read()
4except FileNotFoundError:
5 print("Error: config.txt not found. Please create the configuration file.")
6 sys.exit(1)
7except PermissionError:
8 print("Error: Permission denied reading config.txt. Check file permissions.")
9 sys.exit(1)
Instead of cryptic errors, provide actionable information:
| Bad | Good |
|---|---|
| “Permission denied” | “Unable to write to /tmp. Ensure your user has write permissions on /tmp.” |
| “Connection failed” | “Unable to connect to database server at db.example.com:5432 using username ‘admin’. Check network connectivity and credentials.” |
| “Invalid input” | “Invalid date format. Expected YYYY-MM-DD, received ‘13/11/2025’.” |
For critical failures where the program cannot continue:
1import sys
2
3if not database.is_connected():
4 print("Error: Unable to connect to database server.")
5 print(f"Host: {DB_HOST}")
6 print(f"Port: {DB_PORT}")
7 print(f"Username: {DB_USER}")
8 print("Please verify database server is running and credentials are correct.")
9 sys.exit(1)
| Step | Action |
|---|---|
| 1. Analyze | Read error message and traceback |
| 2. Reproduce | Create minimal reproduction case |
| 3. Debug | Use debugger or logging to trace execution |
| 4. Identify | Locate root cause of error |
| 5. Fix | Correct programming errors |
| 6. Handle | Add exception handling for expected failures |
| 7. Test | Verify fix resolves issue |
| 8. Document | Add comments explaining the fix |
1import logging
2import sys
3
4logging.basicConfig(level=logging.INFO)
5
6def read_config(filename):
7 """Read configuration file with proper error handling."""
8 try:
9 with open(filename, 'r') as f:
10 config = f.read()
11 logging.debug(f"Successfully read {len(config)} bytes from {filename}")
12 return config
13 except FileNotFoundError:
14 logging.error(f"Configuration file '{filename}' not found")
15 print(f"Error: Cannot find {filename}")
16 print("Please create the configuration file or specify correct path.")
17 sys.exit(1)
18 except PermissionError:
19 logging.error(f"Permission denied reading '{filename}'")
20 print(f"Error: Permission denied accessing {filename}")
21 print("Please check file permissions or run with appropriate privileges.")
22 sys.exit(1)
23 except Exception as e:
24 logging.error(f"Unexpected error reading '{filename}': {e}")
25 print(f"Error: Unexpected problem reading {filename}: {e}")
26 sys.exit(1)
27
28if __name__ == "__main__":
29 config = read_config('config.txt')
30 # Process configuration...
Unhandled errors occur when programs encounter unexpected conditions without proper error handling. Effective debugging involves analyzing tracebacks, using interactive debuggers, implementing logging, and making programs resilient through proper exception handling and user-friendly error messages. Well-designed error handling prevents crashes and guides users toward solutions.