Skip to main content
RapidDev - Software Development Agency
replit-tutorial

How to debug advanced Python issues in Replit

Replit supports Python debugging through pdb (Python Debugger), the logging module, traceback analysis, and memory profiling with tracemalloc. Set breakpoints with pdb.set_trace() or breakpoint(), read stack traces to identify the exact line and function causing errors, replace print statements with structured logging, and use tracemalloc to find memory leaks. All of these tools work directly in Replit's Shell and Console without any extensions.

What you'll learn

  • Use pdb to set breakpoints and step through Python code interactively
  • Read and interpret Python traceback error messages accurately
  • Replace print debugging with the structured logging module
  • Profile memory usage with tracemalloc to find leaks and high-memory operations
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read15 minutesAll Replit plans (Starter, Core, Pro, Enterprise)March 2026RapidDev Engineering Team
TL;DR

Replit supports Python debugging through pdb (Python Debugger), the logging module, traceback analysis, and memory profiling with tracemalloc. Set breakpoints with pdb.set_trace() or breakpoint(), read stack traces to identify the exact line and function causing errors, replace print statements with structured logging, and use tracemalloc to find memory leaks. All of these tools work directly in Replit's Shell and Console without any extensions.

How to Debug Advanced Python Issues in Replit

Debugging Python goes beyond adding print statements. This tutorial covers four professional debugging techniques that work inside Replit: the built-in pdb debugger for stepping through code line by line, structured logging for tracking application behavior, traceback analysis for reading error messages effectively, and tracemalloc for identifying memory issues. These skills are essential as your Python projects grow more complex.

Prerequisites

  • A Replit account with a Python project (any plan)
  • Basic Python knowledge including functions, variables, and imports
  • Access to the Shell tab in the Replit workspace
  • Familiarity with running Python scripts from the command line

Step-by-step guide

1

Use pdb to set breakpoints and step through code

Python's built-in debugger pdb lets you pause execution at any line, inspect variables, and step through code one line at a time. Add breakpoint() (Python 3.7+) or import pdb; pdb.set_trace() at the line where you want to pause. Run your script from the Shell (not the Run button) so you can interact with the debugger. When execution pauses, use commands like n (next line), s (step into function), p variable (print value), c (continue), and q (quit). This is far more effective than scattering print statements.

typescript
1# example.py
2def calculate_total(items):
3 total = 0
4 for item in items:
5 breakpoint() # Execution pauses here
6 total += item['price'] * item['quantity']
7 return total
8
9items = [
10 {'name': 'Widget', 'price': 9.99, 'quantity': 3},
11 {'name': 'Gadget', 'price': 24.99, 'quantity': 1},
12 {'name': 'Doohickey', 'price': None, 'quantity': 2}, # Bug: None price
13]
14
15result = calculate_total(items)
16print(f'Total: ${result:.2f}')

Expected result: Execution pauses at the breakpoint. You can type p item to see the current item, p total to see the running total, and n to proceed to the next line.

2

Read Python traceback messages to locate errors

When Python crashes, it prints a traceback showing the chain of function calls that led to the error. Read tracebacks from bottom to top: the last line is the actual error type and message, and the lines above show each function call with file name and line number. The most recent call is at the bottom. Common error types are TypeError (wrong data type), KeyError (missing dictionary key), AttributeError (calling a method on the wrong type), and IndexError (list index out of range). Understanding tracebacks lets you go straight to the bug instead of guessing.

typescript
1# This code produces a traceback:
2def get_user_email(users, user_id):
3 user = users[user_id] # Line that crashes
4 return user['email']
5
6users = {'alice': {'email': 'alice@example.com'}}
7email = get_user_email(users, 'bob') # 'bob' not in dict
8
9# Traceback output:
10# Traceback (most recent call last):
11# File "main.py", line 6, in <module>
12# email = get_user_email(users, 'bob')
13# File "main.py", line 2, in get_user_email
14# user = users[user_id]
15# KeyError: 'bob'
16
17# Fix: use .get() with a default
18def get_user_email(users, user_id):
19 user = users.get(user_id)
20 if user is None:
21 return None
22 return user['email']

Expected result: You can read the traceback, identify that the error is a KeyError on line 2 caused by 'bob' not existing in the dictionary, and apply the fix.

3

Replace print statements with the logging module

The logging module provides structured, leveled output that you can filter by severity. Instead of print('something happened'), use logging.info('something happened'). Logging supports levels: DEBUG (detailed), INFO (general), WARNING (potential issues), ERROR (failures), and CRITICAL (fatal). You can configure it to include timestamps, file names, and line numbers automatically. Unlike print statements, logging can be turned off for production by changing the log level without removing any code.

typescript
1import logging
2
3# Configure logging with timestamps and levels
4logging.basicConfig(
5 level=logging.DEBUG,
6 format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
7 datefmt='%H:%M:%S'
8)
9
10logger = logging.getLogger('myapp')
11
12def process_order(order):
13 logger.info(f"Processing order {order['id']}")
14 logger.debug(f"Order details: {order}")
15
16 if order['total'] <= 0:
17 logger.warning(f"Order {order['id']} has zero or negative total")
18 return False
19
20 try:
21 # Simulate payment processing
22 charge_payment(order)
23 logger.info(f"Order {order['id']} completed successfully")
24 return True
25 except Exception as e:
26 logger.error(f"Payment failed for order {order['id']}: {e}")
27 return False

Expected result: Your output shows timestamped, leveled messages like '14:32:01 [INFO] myapp: Processing order 123' that are easy to filter and search.

4

Profile memory usage with tracemalloc

If your Replit app crashes with 'Your Repl ran out of memory', you need to find what is consuming RAM. Python's built-in tracemalloc module tracks memory allocations and shows you exactly which lines of code allocate the most memory. Start tracing at the beginning of your script, run the memory-intensive operation, then take a snapshot and display the top allocations. This is especially important on Replit's Starter plan with only 2 GiB RAM.

typescript
1import tracemalloc
2
3# Start tracing memory allocations
4tracemalloc.start()
5
6def load_large_dataset():
7 # Simulating a memory-heavy operation
8 data = []
9 for i in range(100000):
10 data.append({'id': i, 'value': f'item_{i}' * 100})
11 return data
12
13# Run the operation
14result = load_large_dataset()
15
16# Take a snapshot and show top memory consumers
17snapshot = tracemalloc.take_snapshot()
18stats = snapshot.statistics('lineno')
19
20print('\nTop 10 memory allocations:')
21for stat in stats[:10]:
22 print(f' {stat}')
23
24# Show current and peak memory usage
25current, peak = tracemalloc.get_traced_memory()
26print(f'\nCurrent memory: {current / 1024 / 1024:.1f} MB')
27print(f'Peak memory: {peak / 1024 / 1024:.1f} MB')
28
29tracemalloc.stop()

Expected result: You see a ranked list of memory allocations by file and line number, plus total current and peak memory usage in megabytes.

5

Debug API and network errors with exception chaining

When your Python app calls external APIs, errors can be vague. Use try-except blocks with detailed logging to capture the actual error message, status code, and response body. Python 3 supports exception chaining with raise ... from to preserve the original error context. For HTTP requests using the requests library, always check the status code and log the response text on failure before raising an error.

typescript
1import requests
2import logging
3
4logger = logging.getLogger('api')
5
6def fetch_weather(city):
7 api_key = os.getenv('WEATHER_API_KEY')
8 if not api_key:
9 raise ValueError('WEATHER_API_KEY not set. Add it in Tools → Secrets.')
10
11 url = f'https://api.example.com/weather?q={city}&key={api_key}'
12 logger.info(f'Fetching weather for {city}')
13
14 try:
15 response = requests.get(url, timeout=10)
16 logger.debug(f'Response status: {response.status_code}')
17
18 if response.status_code != 200:
19 logger.error(f'API error {response.status_code}: {response.text}')
20 response.raise_for_status()
21
22 return response.json()
23 except requests.Timeout:
24 logger.error(f'Request to weather API timed out after 10 seconds')
25 raise
26 except requests.ConnectionError as e:
27 logger.error(f'Cannot connect to weather API: {e}')
28 raise

Expected result: When an API call fails, your logs show the exact status code, response body, and error type instead of a generic exception.

Complete working example

debug_utils.py
1"""debug_utils.py Python debugging utilities for Replit projects.
2
3Import these helpers during development.
4Set DEBUG_MODE=true in Tools Secrets to enable verbose output.
5"""
6import os
7import logging
8import tracemalloc
9from functools import wraps
10import time
11
12# Configure logging based on Secrets
13log_level = logging.DEBUG if os.getenv('DEBUG_MODE') == 'true' else logging.INFO
14logging.basicConfig(
15 level=log_level,
16 format='%(asctime)s [%(levelname)s] %(name)s (%(filename)s:%(lineno)d): %(message)s',
17 datefmt='%H:%M:%S'
18)
19logger = logging.getLogger('app')
20
21
22def timed(func):
23 """Decorator that logs how long a function takes to execute."""
24 @wraps(func)
25 def wrapper(*args, **kwargs):
26 start = time.time()
27 try:
28 result = func(*args, **kwargs)
29 elapsed = time.time() - start
30 logger.debug(f'{func.__name__} completed in {elapsed:.3f}s')
31 return result
32 except Exception as e:
33 elapsed = time.time() - start
34 logger.error(f'{func.__name__} failed after {elapsed:.3f}s: {e}')
35 raise
36 return wrapper
37
38
39def log_memory(label=''):
40 """Print current and peak memory usage."""
41 if not tracemalloc.is_tracing():
42 tracemalloc.start()
43 current, peak = tracemalloc.get_traced_memory()
44 logger.info(
45 f'Memory [{label}]: current={current/1024/1024:.1f}MB, '
46 f'peak={peak/1024/1024:.1f}MB'
47 )
48
49
50def top_memory_allocations(limit=10):
51 """Show the top memory-consuming lines of code."""
52 if not tracemalloc.is_tracing():
53 logger.warning('tracemalloc not started. Call tracemalloc.start() first.')
54 return
55 snapshot = tracemalloc.take_snapshot()
56 stats = snapshot.statistics('lineno')
57 logger.info(f'Top {limit} memory allocations:')
58 for stat in stats[:limit]:
59 logger.info(f' {stat}')
60
61
62def safe_request(url, method='GET', timeout=10, **kwargs):
63 """Make an HTTP request with full error logging."""
64 import requests
65 logger.info(f'{method} {url}')
66 try:
67 response = requests.request(method, url, timeout=timeout, **kwargs)
68 logger.debug(f'Response: {response.status_code}')
69 if not response.ok:
70 logger.error(f'{response.status_code}: {response.text[:500]}')
71 return response
72 except requests.Timeout:
73 logger.error(f'Timeout after {timeout}s: {url}')
74 raise
75 except requests.ConnectionError as e:
76 logger.error(f'Connection failed: {e}')
77 raise

Common mistakes when debugging advanced Python issues in Replit

Why it's a problem: Using the Run button to debug with pdb, which does not support interactive input in Console

How to avoid: Run your script from the Shell tab with python filename.py so you can type pdb commands

Why it's a problem: Leaving breakpoint() calls in code that gets deployed to production

How to avoid: Search your codebase for breakpoint() and pdb.set_trace() before deploying, and remove or conditionally disable them

Why it's a problem: Catching all exceptions with a bare except: clause that hides the actual error

How to avoid: Catch specific exception types like except ValueError as e and always log the error message

Why it's a problem: Using print(object) for debugging dictionaries and getting unhelpful output

How to avoid: Use import json; print(json.dumps(obj, indent=2, default=str)) for readable, formatted output

Why it's a problem: Not setting timeouts on requests.get() calls, causing the Repl to hang indefinitely

How to avoid: Always pass timeout=10 (or an appropriate value) to requests.get() and requests.post()

Best practices

  • Run pdb from the Shell tab, not the Run button, because Console does not support interactive debugger input
  • Read Python tracebacks from bottom to top: the last line is the error, the lines above show the call chain
  • Use the logging module with severity levels instead of print statements for structured, filterable output
  • Set a DEBUG_MODE secret in Tools → Secrets to toggle verbose logging without changing code
  • Always set timeouts on HTTP requests to prevent frozen Repls from consuming resources
  • Use tracemalloc proactively before your app hits the memory limit, especially on the 2 GiB Starter plan
  • Add type hints to function signatures so tracebacks are easier to understand when type errors occur
  • Delete breakpoint() calls before deploying — they will freeze your production app

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My Python script on Replit crashes with 'TypeError: unsupported operand type(s)' but the traceback points to a line inside a loop. How do I use pdb to find which iteration of the loop contains the bad data? Show me the pdb commands to inspect the current variable values.

Replit Prompt

My Python app is using too much memory and crashing with 'Your Repl ran out of memory'. Add tracemalloc to my main.py to track memory allocations. Show me the top 10 memory-consuming lines and current vs peak memory usage. Also add a @timed decorator to the three largest functions.

Frequently asked questions

Replit does not have a built-in visual debugger with a GUI. However, Python's built-in pdb works in the Shell tab, providing interactive breakpoints, variable inspection, and step-through execution. For complex debugging needs, teams can consult RapidDev for guidance on advanced debugging strategies.

The Run button sends output to the Console tab, which is read-only and does not support interactive input. Run your script from the Shell tab instead with python filename.py so you can type pdb commands.

Use the tracemalloc module. Call tracemalloc.start() at the beginning of your script, take snapshots before and after operations, and compare them with snapshot2.compare_to(snapshot1, 'lineno') to see which lines increased memory usage.

Yes, you can configure a FileHandler in logging. However, remember that the file system resets on deployment. For persistent logs in production, use an external logging service or write to the database.

The Starter plan provides 2 GiB RAM. Core provides 8 GiB. Pro provides 8 GiB or more. If your Python app exceeds the limit, Replit kills the process with 'Your Repl ran out of memory'.

A ModuleNotFoundError means the package is not installed. Run pip install package_name in Shell. If it was installed but still fails, check for virtual environment conflicts by running which python and pip list in Shell.

Yes. Install them with pip install ipdb or pip install pdbpp in Shell. Use import ipdb; ipdb.set_trace() for a more feature-rich debugging experience with syntax highlighting and tab completion.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.