Unhandled Errors

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.


Introduction

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.


Understanding Errors and Exceptions

Memory Management in High-Level Languages

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.

Common Python Error Types

Error TypeCauseExample
IndexErrorAccessing list element beyond valid rangelist[10] when list has 5 elements
TypeErrorOperation on incompatible typeAdding string to integer
AttributeErrorAccessing non-existent attributeCalling method on uninitialized variable
ZeroDivisionErrorDivision by zerox / 0
KeyErrorAccessing non-existent dictionary keydict['missing_key']
ValueErrorInvalid value for operationint('abc')

Root Causes of Unhandled Errors

Unhandled errors typically occur because code makes incorrect assumptions:

  • Attempting to access resources that don’t exist
  • Assuming user input when user provides empty string
  • Converting values between formats when data doesn’t match expectations
  • Operating on variables before proper initialization

Tracebacks and Error Messages

When unhandled errors occur, the interpreter provides diagnostic information:

Information Provided

  • Error type (e.g., IndexError, TypeError)
  • Line number where failure occurred
  • Traceback showing function call stack

Understanding Tracebacks

The traceback displays the sequence of function calls leading to the error. This is crucial because:

  • The crash location may not be where the bug originated
  • Earlier functions might have set variables to incorrect values
  • The crashing function might only be accessing those bad values

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

Debugging Techniques

Interactive Debuggers

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()

PDB Capabilities

CommandPurpose
n (next)Execute next line
s (step)Step into function
c (continue)Continue execution
p variablePrint variable value
l (list)Show current code context
w (where)Show stack trace

Printf Debugging

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:

  • Variable contents
  • Function return values
  • Metadata (list length, file size)

The name originates from C’s printf function, but applies to any language using print, puts, echo, or similar output functions.


Logging Module

Advantages Over Print Statements

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")

Log Levels

LevelValueUsage
DEBUG10Detailed diagnostic information
INFO20General informational messages
WARNING30Warning messages for potential issues
ERROR40Error messages for serious problems
CRITICAL50Critical problems causing program failure

Configuration Benefits

Logging level can be changed via:

  • Command-line flags
  • Configuration files
  • Environment variables

This allows debug messages during development without cluttering production output.


Making Programs Resilient

Fixing Programming Errors

Common fixes include:

  • Initializing variables before use
  • Validating list/array bounds before access
  • Checking for None/null values
  • Validating user input

Handling Unexpected Conditions

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)

User-Friendly Error Messages

Instead of cryptic errors, provide actionable information:

BadGood
“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’.”

Graceful Termination

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)

Debugging Workflow

Step-by-Step Process

StepAction
1. AnalyzeRead error message and traceback
2. ReproduceCreate minimal reproduction case
3. DebugUse debugger or logging to trace execution
4. IdentifyLocate root cause of error
5. FixCorrect programming errors
6. HandleAdd exception handling for expected failures
7. TestVerify fix resolves issue
8. DocumentAdd comments explaining the fix

Example: Complete Error Handling

 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...

Conclusion

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.


FAQ