debug:flask

📁 snakeo/claude-debug-and-refactor-skills-plugin 📅 Jan 19, 2026
0
总安装量
17
周安装量
安装命令
npx skills add https://github.com/snakeo/claude-debug-and-refactor-skills-plugin --skill debug:flask

Agent 安装分布

claude-code 15
opencode 14
gemini-cli 13
codex 12
antigravity 11

Skill 文档

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_slashes setting)
  • 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_app outside 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, or g outside 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() or db.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.py shadowing 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 routes to 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 shell to test in isolation?
  • Did you check the browser’s Network tab for request details?
  • Are database migrations up to date? (flask db upgrade)

References