Introduction
Flask separates application context and request context. The application context holds app-level data (config, database connections, extensions) while the request context holds request-specific data. When code tries to access current_app, g, or extension methods outside an active application context -- such as in background threads, Celery tasks, or CLI commands -- Flask raises RuntimeError: Working outside of application context. This is a fundamental design pattern in Flask that prevents implicit global state but is a common source of confusion when moving code out of request handlers.
Symptoms
``` RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed the current application. To solve this, set up an application context with app.app_context(). See the documentation for more information. ```
Or for request context:
``` RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed information about the current request. ```
Common Causes
- Background thread accessing current_app: Thread started outside Flask request
- Celery task without app context: Celery workers do not have Flask context
- CLI command accessing extensions: Flask-Migrate, Flask-SQLAlchemy need app context
- Blueprint created before app factory: Extension initialized with no app
- Import-time extension usage: Extension accessed at module load, before app created
- Testing without test client: Testing code directly instead of through test client
Step-by-Step Fix
Step 1: Use app_context() explicitly
```python from flask import current_app from myapp import create_app
# WRONG: Accessing current_app outside context # print(current_app.config['SECRET_KEY']) # Raises RuntimeError
# CORRECT: Wrap in app_context app = create_app() with app.app_context(): # Now current_app works print(current_app.config['SECRET_KEY']) db.create_all() # SQLAlchemy operations need app context ```
Step 2: Background tasks with app context
```python import threading from flask import current_app
def send_email_async(to, subject, body): """Background task that needs Flask app context.""" # Push app context into the new thread from myapp import create_app app = create_app() with app.app_context(): # Access extensions safely mail = current_app.extensions.get('mail') mail.send_message(subject, recipients=[to], body=body)
# Start background thread thread = threading.Thread(target=send_email_async, args=( 'user@example.com', 'Welcome', 'Hello!' )) thread.start() ```
Step 3: Celery tasks with Flask app context
```python from celery import Celery from myapp import create_app
def make_celery(app=None): celery = Celery( 'myapp', broker=app.config.get('CELERY_BROKER_URL', 'redis://localhost:6379/0'), backend=app.config.get('CELERY_RESULT_BACKEND', 'redis://localhost:6379/0'), )
class ContextTask(celery.Task): def __call__(self, *args, **kwargs): # Push app context for every task execution with app.app_context(): return self.run(*args, **kwargs)
celery.Task = ContextTask return celery
# Usage app = create_app() celery = make_celery(app)
@celery.task def process_data(item_id): # current_app and db are available here from flask import current_app item = db.session.query(Item).get(item_id) return process(item) ```
Prevention
- Use app factory pattern and always call create_app() before accessing extensions
- Push app context with
app.app_context()in background threads and CLI commands - Use Flask's test client (
app.test_client()) instead of calling view functions directly - Structure blueprints so they do not access app-level state at import time
- Use
@app.before_first_requestor startup hooks for initialization that needs context - Add context checks to your test suite to catch missing context early
- Document which functions require app context vs request context