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_request or 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