# How to trace runtime errors in Replit

- Tool: Replit
- Difficulty: Beginner
- Time required: 15-20 minutes
- Compatibility: All Replit plans (Starter, Core, Pro). Python 3.x required. Works with any Python project.
- Last updated: March 2026

## TL;DR

When your Python script crashes in Replit, the Console shows a traceback — a stack of error messages that tells you exactly which file, line number, and function caused the failure. This tutorial teaches you to read tracebacks from bottom to top, use the logging module for structured error tracking, and add try/except blocks to handle errors gracefully instead of crashing.

## Read Python Tracebacks and Debug Runtime Errors in Replit

This tutorial teaches you how to find and fix Python runtime errors using Replit's Console output and Shell. You will learn to read Python tracebacks, understand the most common error types (NameError, TypeError, KeyError, AttributeError), use the logging module for structured error tracking, and add error handling to prevent crashes. Every debugging technique works entirely within the browser — no external tools or terminal setup required.

## Before you start

- A Replit account (free Starter plan works)
- A Python Repl with code that produces runtime errors
- Basic Python knowledge (variables, functions, imports)
- Familiarity with the Replit Console and Shell tabs

## Step-by-step guide

### 1. Read the traceback from bottom to top

When your Python script crashes, the Console shows a traceback — a multi-line error message. Most beginners read it top to bottom, but the most important information is at the bottom. The last line tells you the error type and message (e.g., NameError: name 'x' is not defined). The line just above it shows the exact code that caused the error. Lines above that show the call chain — which function called which. Start at the bottom and work your way up to understand how your code reached the failing point.

```
# This code will produce a traceback:
def calculate_total(items):
    total = 0
    for item in items:
        total += item['price'] * item['quantity']
    return total

orders = [
    {'price': 10, 'quantity': 2},
    {'price': 25},  # Missing 'quantity' key!
]

result = calculate_total(orders)

# Traceback output:
# Traceback (most recent call last):
#   File "main.py", line 12, in <module>
#     result = calculate_total(orders)
#   File "main.py", line 4, in calculate_total
#     total += item['price'] * item['quantity']
# KeyError: 'quantity'
```

**Expected result:** You can identify the error type (KeyError), the line number (line 4), the function (calculate_total), and the missing data ('quantity' key) from the traceback.

### 2. Identify the five most common Python runtime errors

Most Python crashes in Replit fall into five categories. NameError means you used a variable that does not exist — usually a typo. TypeError means you passed the wrong data type to a function (like adding a string to a number). KeyError means a dictionary key does not exist. AttributeError means you called a method on an object that does not have it. IndexError means you tried to access a list index that is out of range. Recognizing these error types immediately tells you where to look in your code.

```
# NameError — typo in variable name:
user_name = "Alice"
print(username)  # NameError: name 'username' is not defined

# TypeError — wrong data type:
age = "25"
result = age + 10  # TypeError: can only concatenate str to str

# KeyError — missing dictionary key:
user = {'name': 'Alice'}
print(user['email'])  # KeyError: 'email'

# AttributeError — wrong method on wrong type:
my_list = [1, 2, 3]
my_list.split(',')  # AttributeError: 'list' has no attribute 'split'

# IndexError — list index out of range:
items = ['a', 'b', 'c']
print(items[5])  # IndexError: list index out of range
```

**Expected result:** You can recognize these five error types by name and know what to look for in your code when each one appears in the Console.

### 3. Add logging instead of print statements

While print() works for quick debugging, the logging module provides timestamps, severity levels, and structured output that make it much easier to trace issues in larger programs. Set up logging at the top of your script with basicConfig, then use logging.info() for normal messages, logging.warning() for potential issues, and logging.error() for failures. Log output appears in the Console alongside your regular output but with timestamps and levels that help you reconstruct what happened when something goes wrong.

```
import logging

# Set up logging with timestamps and severity levels
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] %(message)s',
    datefmt='%H:%M:%S'
)

def fetch_user(user_id):
    logging.info(f"Fetching user {user_id}")
    
    if not isinstance(user_id, int):
        logging.error(f"Invalid user_id type: {type(user_id).__name__}")
        return None
    
    # Simulate database lookup
    users = {1: 'Alice', 2: 'Bob'}
    user = users.get(user_id)
    
    if user is None:
        logging.warning(f"User {user_id} not found")
    else:
        logging.info(f"Found user: {user}")
    
    return user

fetch_user(1)    # INFO: Found user: Alice
fetch_user(99)   # WARNING: User 99 not found
fetch_user('x')  # ERROR: Invalid user_id type: str
```

**Expected result:** The Console shows timestamped log entries with severity levels like [INFO], [WARNING], and [ERROR] instead of plain print output.

### 4. Wrap risky code in try/except blocks

Instead of letting your script crash when an error occurs, wrap risky operations in try/except blocks to catch errors and handle them gracefully. This is especially important for code that makes API calls, reads files, or processes user input. Catch specific exception types rather than using bare except — this prevents masking unexpected bugs. Log the error details with logging.exception() which includes the full traceback in the log output.

```
import logging
import json

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] %(message)s'
)

def process_api_response(raw_data):
    try:
        data = json.loads(raw_data)
        user_name = data['user']['name']
        logging.info(f"Processed user: {user_name}")
        return user_name
    except json.JSONDecodeError as e:
        logging.error(f"Invalid JSON response: {e}")
        return None
    except KeyError as e:
        logging.error(f"Missing key in response: {e}")
        return None
    except Exception as e:
        logging.exception(f"Unexpected error processing response")
        return None

# Test with valid data:
process_api_response('{"user": {"name": "Alice"}}')

# Test with invalid JSON:
process_api_response('not json')

# Test with missing key:
process_api_response('{"user": {}}')
```

**Expected result:** The script handles all three error cases without crashing. Each error is logged with a descriptive message, and the function returns None instead of raising an exception.

### 5. Use sys.exc_info() for detailed error inspection

For complex debugging scenarios, Python's sys.exc_info() function gives you detailed information about the current exception including the exception type, value, and full traceback object. This is useful when you need to inspect or forward error details programmatically, such as sending error reports to an external service or creating custom error pages. Use it inside an except block to capture the active exception.

```
import sys
import traceback
import logging

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(levelname)s] %(message)s')

def risky_operation():
    data = {'a': 1}
    return data['b']  # Will raise KeyError

try:
    risky_operation()
except Exception:
    exc_type, exc_value, exc_tb = sys.exc_info()
    
    logging.error(f"Exception type: {exc_type.__name__}")
    logging.error(f"Exception message: {exc_value}")
    
    # Get the full traceback as a string
    tb_lines = traceback.format_exception(exc_type, exc_value, exc_tb)
    full_traceback = ''.join(tb_lines)
    logging.error(f"Full traceback:\n{full_traceback}")
    
    # Extract the specific file and line number
    tb_details = traceback.extract_tb(exc_tb)
    for filename, line, func, text in tb_details:
        logging.error(f"  File: {filename}, Line: {line}, Function: {func}")
        logging.error(f"  Code: {text}")
```

**Expected result:** The Console shows the exception type, message, full traceback, and individual file/line/function details for each frame in the call stack.

## Complete code example

File: `debug_example.py`

```python
# debug_example.py — Complete Python debugging template for Replit
import os
import sys
import logging
import traceback

# Configure logging with timestamps and severity levels
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] %(funcName)s: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logger = logging.getLogger(__name__)


def safe_get_env(key, default=None):
    """Safely get an environment variable with logging."""
    value = os.getenv(key, default)
    if value is None:
        logger.warning(f"Environment variable '{key}' not set")
    else:
        logger.debug(f"Loaded env var: {key}")
    return value


def process_data(records):
    """Process a list of records with error handling."""
    logger.info(f"Processing {len(records)} records")
    results = []

    for i, record in enumerate(records):
        try:
            name = record['name']
            score = int(record['score'])
            grade = 'Pass' if score >= 60 else 'Fail'
            results.append({'name': name, 'score': score, 'grade': grade})
            logger.debug(f"Record {i}: {name} = {grade}")

        except KeyError as e:
            logger.error(f"Record {i} missing required key: {e}")
            logger.error(f"Record contents: {record}")

        except (ValueError, TypeError) as e:
            logger.error(f"Record {i} has invalid score value: {e}")

        except Exception:
            logger.exception(f"Unexpected error processing record {i}")

    logger.info(f"Successfully processed {len(results)}/{len(records)} records")
    return results


def main():
    logger.info("Application started")
    logger.info(f"Python version: {sys.version}")

    # Load configuration from environment
    api_key = safe_get_env('API_KEY')
    debug_mode = safe_get_env('DEBUG', 'false') == 'true'

    # Sample data — some records have deliberate errors
    test_data = [
        {'name': 'Alice', 'score': '95'},
        {'name': 'Bob', 'score': '72'},
        {'name': 'Charlie'},              # Missing 'score'
        {'name': 'Diana', 'score': 'N/A'},  # Invalid score
        {'name': 'Eve', 'score': '88'},
    ]

    results = process_data(test_data)

    print("\n=== Results ===")
    for r in results:
        print(f"  {r['name']}: {r['score']} ({r['grade']})")

    logger.info("Application finished")


if __name__ == '__main__':
    main()
```

## Common mistakes

- **Reading tracebacks from top to bottom and getting lost in the call chain** — undefined Fix: Always start at the bottom line. The last line shows the error type and message. Work upward to find the file, line number, and function that caused it.
- **Using bare except: blocks that catch everything including KeyboardInterrupt** — undefined Fix: Always specify the exception type: except KeyError as e: or except (ValueError, TypeError) as e:. Use except Exception as a last resort.
- **Using only print() for debugging and losing track of output in large programs** — undefined Fix: Switch to the logging module with timestamps and severity levels. It makes it much easier to find errors in output that may span hundreds of lines.
- **Catching exceptions silently without logging or re-raising them** — undefined Fix: Always log the error details inside except blocks. Silent exception handling hides bugs and makes them impossible to diagnose later.

## Best practices

- Read tracebacks from bottom to top — the last line has the error type and the line above it has the failing code
- Use the logging module instead of print() for any project larger than a single script
- Catch specific exception types (KeyError, ValueError) not bare except clauses
- Include the failing data in your log messages so you can reproduce the issue
- Set logging level to DEBUG during development and WARNING in production
- Use logging.exception() inside except blocks — it automatically includes the full traceback
- Check the Console tab (not just Shell) when running your app with the Run button to see structured output

## Frequently asked questions

### Where do Python errors appear in Replit?

When you press the Run button, Python errors (tracebacks) appear in the Console tab. If you run scripts manually in the Shell tab, errors appear in Shell output. Client-side JavaScript errors appear only in the Preview's browser DevTools, not the Console.

### What is the difference between Console and Shell for debugging?

The Console shows output from the Run button with structured entries (stdout, stderr, status). The Shell is an interactive terminal where you can run commands directly. For debugging, the Console is best for running your app; Shell is best for running individual test commands.

### Can Replit Agent debug my Python errors?

Yes. Paste the traceback into the Agent chat and ask: 'This script is throwing this error. Fix the bug and add error handling so it does not crash.' Agent v4 will read the traceback, locate the issue, and apply a fix.

### How do I debug Python errors in a deployed Replit app?

Check the Logs tab in the Deployments pane. All print() and logging output from your deployed app appears there. If logging is not set up, add the logging module so errors are captured with timestamps and context.

### Does Replit have a visual debugger for Python?

Replit does not include a step-through visual debugger like VS Code's Debug panel as of March 2026. You debug using Console output, print statements, and the logging module. For step-through debugging, use Python's built-in pdb by adding 'import pdb; pdb.set_trace()' in your code and running from Shell.

### Why does my script work in Shell but fail when I press Run?

The Run button uses the command defined in the .replit file's run key, which may point to a different file or use different arguments than what you typed in Shell. Check your .replit file to verify the entrypoint and run command match what you expect.

### How do I get help with persistent Python bugs that I cannot solve?

If standard debugging approaches are not resolving the issue, the RapidDev engineering team can review your code, identify root causes, and implement fixes for complex Python errors that go beyond simple traceback reading.

### What is logging.exception() and when should I use it?

logging.exception() logs a message at ERROR level and automatically appends the current exception's traceback. Use it inside except blocks when you want to log both your custom message and the full stack trace. It is equivalent to logging.error(msg, exc_info=True).

---

Source: https://www.rapidevelopers.com/replit-tutorial/how-to-trace-runtime-errors-in-a-python-script-using-replit-s-debugging-tools
© RapidDev — https://www.rapidevelopers.com/replit-tutorial/how-to-trace-runtime-errors-in-a-python-script-using-replit-s-debugging-tools
