
debug:flask
Debug Flask applications systematically with this comprehensive troubleshooting skill. Covers routing errors (404/405), Jinja2 template issues, application context problems, SQLAlchemy session management, blueprint registration failures, and circular import resolution. Provides structured four-phase debugging methodology with Flask-specific tools including Werkzeug debugger, Flask-DebugToolbar, and Flask shell for interactive investigation.
Debug Flask applications systematically with this comprehensive troubleshooting skill. Covers routing errors (404/405), Jinja2 template issues, application context problems, SQLAlchemy session management, blueprint registration failures, and circular import resolution. Provides structured four-phase debugging methodology with Flask-specific tools including Werkzeug debugger, Flask-DebugToolbar, and Flask shell for interactive investigation.
Flask Debugging Guide
A systematic approach to debugging Flask applications using established patterns and Flask-specific tooling.
Common Error Patterns
HTTP Status Code Errors
404 Not Found - Routing Issues
- Route decorator not matching requested URL
- Typos in route definitions or endpoint names
- Blueprint not registered with application
- Missing trailing slash mismatch (
strict_slashessetting) - URL rules registered after first request
500 Internal Server Error
- Unhandled exceptions in view functions
- Template rendering errors
- Database connection failures
- Malformed JSON request data (missing
Content-Type: application/json) - Circular imports preventing app initialization
405 Method Not Allowed
- HTTP method not specified in
@app.route()methods parameter - Form submission using wrong method (GET vs POST)
Jinja2 Template Errors
TemplateNotFound
- Template not in
templates/directory - Custom template folder not configured
- Blueprint template folder misconfigured
UndefinedError
- Variable not passed to
render_template() - Typo in template variable name
- Accessing attribute on None object
TemplateSyntaxError
- Unclosed blocks (
{% if %}without{% endif %}) - Invalid filter usage
- Incorrect macro definitions
Application Context Errors
RuntimeError: Working outside of application context
- Accessing
current_appoutside request/CLI context - Database operations outside
with app.app_context(): - Celery tasks not properly configured with app context
RuntimeError: Working outside of request context
- Accessing
request,session, orgoutside view function - Background thread without request context
SQLAlchemy Session Issues
DetachedInstanceError
- Accessing lazy-loaded relationship outside session
- Object expired after transaction commit
- Session closed prematurely
InvalidRequestError: Object already attached to session
- Adding object already in different session
- Improper session management in tests
OperationalError: Connection pool exhausted
- Connections not being returned to pool
- Missing
db.session.close()ordb.session.remove() - Long-running transactions
Blueprint Registration Problems
Blueprint registration failures
- Circular imports between blueprints
- Duplicate blueprint names
- Blueprint registered after first request
- URL prefix conflicts
Import and Module Errors
ImportError: cannot import name 'Flask'
- Circular dependency in modules
- File named
flask.pyshadowing the package - Virtual environment not activated
ModuleNotFoundError: No module named 'flask'
- Virtual environment not activated
- Flask not installed in current environment
- Wrong Python interpreter
Debugging Tools
Built-in Flask Debugger (Werkzeug)
# Enable debug mode (DEVELOPMENT ONLY - NEVER IN PRODUCTION)
# Method 1: Environment variable
# export FLASK_DEBUG=1
# Method 2: In code (not recommended)
app.run(debug=True)
# Method 3: Flask CLI
# flask run --debug
The Werkzeug debugger provides:
- Interactive traceback with code context
- In-browser Python REPL at each stack frame
- Variable inspection at each level
- PIN-protected access (check terminal for PIN)
Security Warning: Never enable debug mode in production. The debugger allows arbitrary code execution from the browser.
Python Debugger (pdb/breakpoint)
# Insert breakpoint in code
def my_view():
data = get_data()
breakpoint() # Python 3.7+ (or: import pdb; pdb.set_trace())
return process(data)
# Common pdb commands:
# n (next) - Execute next line
# s (step) - Step into function
# c (continue) - Continue to next breakpoint
# p variable - Print variable value
# pp variable - Pretty print variable
# l (list) - Show current code context
# w (where) - Show stack trace
# q (quit) - Quit debugger
Flask Shell
# Start interactive shell with app context
flask shell
# In shell:
>>> from app.models import User
>>> User.query.all()
>>> app.config['DEBUG']
>>> app.url_map # View all registered routes
Logging Module
import logging
from flask import Flask
app = Flask(__name__)
# Configure logging
logging.basicConfig(level=logging.DEBUG)
app.logger.setLevel(logging.DEBUG)
# Add file handler for persistent logs
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.WARNING)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
app.logger.addHandler(file_handler)
# Usage in views
@app.route('/api/data')
def get_data():
app.logger.debug('Processing request for /api/data')
try:
result = fetch_data()
app.logger.info(f'Successfully fetched {len(result)} records')
return jsonify(result)
except Exception as e:
app.logger.error(f'Error fetching data: {e}', exc_info=True)
raise
Flask-DebugToolbar
# Install: pip install flask-debugtoolbar
from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
app.config['SECRET_KEY'] = 'dev-secret-key'
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
toolbar = DebugToolbarExtension(app)
Provides sidebar panels for:
- Request/response headers
- Template rendering details
- SQLAlchemy queries (with query time)
- Route matching information
- Configuration values
- Logging messages
SQLAlchemy Query Logging
# Enable SQL query logging
import logging
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
# Or in Flask config
app.config['SQLALCHEMY_ECHO'] = True
# For query analysis with flask-debugtoolbar
app.config['DEBUG_TB_PANELS'] = [
'flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel',
]
The Four Phases of Flask Debugging
Phase 1: Reproduce and Isolate
Capture the exact error state:
# Check Flask is running with debug enabled
flask run --debug
# View registered routes
flask routes
# Check configuration
flask shell
>>> app.config
Minimal reproduction:
# Create minimal test case
def test_failing_route(client):
response = client.get('/api/broken-endpoint')
print(f"Status: {response.status_code}")
print(f"Data: {response.get_data(as_text=True)}")
assert response.status_code == 200
Questions to answer:
- Does error occur on every request or intermittently?
- What is the exact HTTP status code?
- What does the traceback show?
- What was the request payload?
- What environment variables are set?
Phase 2: Gather Information
Collect comprehensive context:
# Add request logging middleware
@app.before_request
def log_request_info():
app.logger.debug('Headers: %s', dict(request.headers))
app.logger.debug('Body: %s', request.get_data())
app.logger.debug('Args: %s', dict(request.args))
@app.after_request
def log_response_info(response):
app.logger.debug('Response Status: %s', response.status)
return response
Check configuration:
flask shell
>>> from flask import current_app
>>> current_app.config['SQLALCHEMY_DATABASE_URI']
>>> current_app.url_map
>>> current_app.extensions
Database state:
# In flask shell
>>> from app import db
>>> db.session.execute(text("SELECT 1")).fetchone() # Test connection
>>> User.query.count()
>>> db.engine.pool.status() # Connection pool status
Phase 3: Hypothesize and Test
Form hypotheses based on error patterns:
| Error Pattern | Likely Cause | Test |
|---|---|---|
| 404 on valid route | Blueprint not registered | Check app.url_map |
| 500 with no traceback | Error before request context | Check create_app() |
| DetachedInstanceError | Lazy load outside session | Add lazy='joined' |
| Connection refused | DB not running | Test connection string |
Test hypotheses systematically:
# Hypothesis: Route not registered
flask routes | grep expected_endpoint
# Hypothesis: Database connection issue
flask shell
>>> from app import db
>>> db.session.execute(text("SELECT 1"))
# Hypothesis: Configuration not loaded
flask shell
>>> import os
>>> os.environ.get('DATABASE_URL')
>>> app.config.get('DATABASE_URL')
Phase 4: Fix and Verify
Apply fix with minimal changes:
# Before fixing, add test to capture expected behavior
def test_user_creation(client):
response = client.post('/api/users', json={'name': 'Test'})
assert response.status_code == 201
assert 'id' in response.get_json()
# After fix, verify test passes
pytest tests/test_users.py::test_user_creation -v
Verify fix doesn't introduce regressions:
# Run full test suite
pytest
# Run with coverage to ensure fix is tested
pytest --cov=app --cov-report=term-missing
Document the fix:
# Add comment explaining the fix
@app.route('/api/users', methods=['POST'])
def create_user():
# FIX: Must validate JSON before accessing request.json
# See: https://github.com/org/repo/issues/123
if not request.is_json:
return jsonify(error='Content-Type must be application/json'), 400
...
Quick Reference Commands
# View all registered routes
flask routes
# Interactive shell with app context
flask shell
# Database migration status (Flask-Migrate)
flask db status
flask db current
flask db history
# Run with debug mode
flask run --debug
# Run with specific host/port
flask run --host=0.0.0.0 --port=5000
# Show Flask configuration
flask shell -c "print(app.config)"
# Test database connection
flask shell -c "from app import db; print(db.session.execute(text('SELECT 1')).fetchone())"
# Check environment variables
flask shell -c "import os; print(os.environ.get('FLASK_ENV'))"
# List installed extensions
flask shell -c "print(list(app.extensions.keys()))"
Flask-Specific Debugging Patterns
Debug Application Factory Issues
# Common issue: app not created properly
def create_app(config_name='default'):
app = Flask(__name__)
# Debug: Print when config loads
print(f"Loading config: {config_name}")
app.config.from_object(config[config_name])
# Debug: Verify extensions initialized
db.init_app(app)
print(f"DB initialized: {db}")
# Debug: Verify blueprints registered
from app.api import api_bp
app.register_blueprint(api_bp, url_prefix='/api')
print(f"Registered routes: {[r.rule for r in app.url_map.iter_rules()]}")
return app
Debug Request/Response Cycle
from flask import g, request
import time
@app.before_request
def before_request():
g.start_time = time.time()
g.request_id = request.headers.get('X-Request-ID', 'unknown')
app.logger.info(f"[{g.request_id}] {request.method} {request.path}")
@app.after_request
def after_request(response):
duration = time.time() - g.start_time
app.logger.info(
f"[{g.request_id}] {response.status_code} ({duration:.3f}s)"
)
return response
@app.teardown_request
def teardown_request(exception):
if exception:
app.logger.error(f"[{g.request_id}] Exception: {exception}")
Debug SQLAlchemy N+1 Queries
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import event
db = SQLAlchemy()
# Log all queries in development
if app.debug:
@event.listens_for(db.engine, "before_cursor_execute")
def receive_before_cursor_execute(conn, cursor, statement, parameters, context, executemany):
app.logger.debug(f"SQL: {statement}")
app.logger.debug(f"Params: {parameters}")
Debug Template Rendering
# Add context processor to expose debug info
@app.context_processor
def debug_context():
if app.debug:
return {
'debug_info': {
'endpoint': request.endpoint,
'view_args': request.view_args,
'url': request.url,
}
}
return {}
<!-- In template, show debug info -->
{% if config.DEBUG %}
<pre>
Endpoint: {{ debug_info.endpoint }}
View Args: {{ debug_info.view_args }}
URL: {{ debug_info.url }}
</pre>
{% endif %}
Debug Blueprint Registration
def register_blueprints(app):
"""Register all blueprints with debugging."""
from app.api import api_bp
from app.auth import auth_bp
blueprints = [
(api_bp, '/api'),
(auth_bp, '/auth'),
]
for bp, prefix in blueprints:
app.logger.debug(f"Registering blueprint: {bp.name} at {prefix}")
try:
app.register_blueprint(bp, url_prefix=prefix)
app.logger.debug(f"Successfully registered {bp.name}")
except Exception as e:
app.logger.error(f"Failed to register {bp.name}: {e}")
raise
Error Handlers for Better Debugging
from flask import jsonify
from werkzeug.exceptions import HTTPException
@app.errorhandler(Exception)
def handle_exception(e):
"""Handle all unhandled exceptions."""
# Log the full exception with traceback
app.logger.exception(f"Unhandled exception: {e}")
# Pass through HTTP errors
if isinstance(e, HTTPException):
return jsonify(error=e.description), e.code
# Return generic error in production, detailed in development
if app.debug:
return jsonify(
error=str(e),
type=type(e).__name__,
traceback=traceback.format_exc()
), 500
return jsonify(error="Internal server error"), 500
@app.errorhandler(404)
def not_found(e):
app.logger.warning(f"404: {request.url}")
return jsonify(error="Resource not found", path=request.path), 404
@app.errorhandler(500)
def internal_error(e):
db.session.rollback() # Rollback any failed transactions
app.logger.error(f"500 error: {e}")
return jsonify(error="Internal server error"), 500
Production Debugging with Sentry
# Install: pip install sentry-sdk[flask]
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
sentry_sdk.init(
dsn="your-sentry-dsn",
integrations=[
FlaskIntegration(),
SqlalchemyIntegration(),
],
traces_sample_rate=0.1, # 10% of transactions for performance monitoring
environment=os.environ.get('FLASK_ENV', 'production'),
)
Environment-Specific Configuration
class Config:
"""Base configuration."""
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key-change-in-prod')
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
"""Development configuration with debugging enabled."""
DEBUG = True
SQLALCHEMY_ECHO = True # Log SQL queries
class TestingConfig(Config):
"""Testing configuration."""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
"""Production configuration - no debug features."""
DEBUG = False
SQLALCHEMY_ECHO = False
# Use proper secret key
SECRET_KEY = os.environ['SECRET_KEY'] # Will raise if not set
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig,
}
Debugging Checklist
Before escalating or spending extensive time:
- [ ] Is Flask running in debug mode? (
FLASK_DEBUG=1) - [ ] Are you in the correct virtual environment?
- [ ] Is the database running and accessible?
- [ ] Have you checked the full traceback in the terminal?
- [ ] Did you run
flask routesto verify route registration? - [ ] Is the request Content-Type correct for JSON endpoints?
- [ ] Have you checked for circular imports?
- [ ] Are all required environment variables set?
- [ ] Is the SECRET_KEY configured?
- [ ] Have you tried
flask shellto test in isolation? - [ ] Did you check the browser's Network tab for request details?
- [ ] Are database migrations up to date? (
flask db upgrade)
References
You Might Also Like
Related Skills

fix
Use when you have lint errors, formatting issues, or before committing code to ensure it passes CI.
facebook
frontend-testing
Generate Vitest + React Testing Library tests for Dify frontend components, hooks, and utilities. Triggers on testing, spec files, coverage, Vitest, RTL, unit tests, integration tests, or write/review test requests.
langgenius
frontend-code-review
Trigger when the user requests a review of frontend files (e.g., `.tsx`, `.ts`, `.js`). Support both pending-change reviews and focused file reviews while applying the checklist rules.
langgenius
code-reviewer
Use this skill to review code. It supports both local changes (staged or working tree) and remote Pull Requests (by ID or URL). It focuses on correctness, maintainability, and adherence to project standards.
google-gemini
session-logs
Search and analyze your own session logs (older/parent conversations) using jq.
moltbot
