django/django · error · RuntimeError
You cannot use ATOMIC_REQUESTS with async views.
Error message
You cannot use ATOMIC_REQUESTS with async views.
What it means
Raised as a RuntimeError by BaseHandler.make_view_atomic when ATOMIC_REQUESTS is enabled on a database connection and the view being wrapped is a coroutine function (async def). Django cannot wrap an async view in transaction.atomic because the sync transaction wrapper would break the async execution model.
Source
Thrown at django/core/handlers/base.py:352
raise ValueError(
"%s didn't return an HttpResponse object. It returned None "
"instead." % name
)
elif asyncio.iscoroutine(response):
raise ValueError(
"%s didn't return an HttpResponse object. It returned an "
"unawaited coroutine instead. You may need to add an 'await' "
"into your view." % name
)
# Other utility methods.
def make_view_atomic(self, view):
non_atomic_requests = getattr(view, "_non_atomic_requests", set())
for alias, settings_dict in connections.settings.items():
if settings_dict["ATOMIC_REQUESTS"] and alias not in non_atomic_requests:
if iscoroutinefunction(view):
raise RuntimeError(
"You cannot use ATOMIC_REQUESTS with async views."
)
view = transaction.atomic(using=alias)(view)
return view
def process_exception_by_middleware(self, exception, request):
"""
Pass the exception to the exception middleware. If no middleware
return a response for this exception, return None.
"""
for middleware_method in self._exception_middleware:
response = middleware_method(request, exception)
if response:
return response
return None
def reset_urlconf(sender, **kwargs):View on GitHub (pinned to ae25a40be0)
Solutions
- Disable ATOMIC_REQUESTS in DATABASES, or set it to False for connections used by async views.
- Mark async views with the 'transaction.non_atomic_requests' attribute so they are excluded: my_view.non_atomic_requests = {'default'} or use the decorator @transaction.non_atomic_requests.
- Manage transactions manually inside the async view using 'async with transaction.atomic():'.
Example fix
// before
# settings.py
DATABASES = {'default': {'ENGINE': ..., 'ATOMIC_REQUESTS': True}}
async def my_view(request): ...
// after
DATABASES = {'default': {'ENGINE': ..., 'ATOMIC_REQUESTS': False}}
# or decorate the async view:
from django.db import transaction
@transaction.non_atomic_requests
async def my_view(request): ... Defensive patterns
Strategy: validation
Validate before calling
import asyncio, inspect
from django.conf import settings
def check_async_views_atomic_conflict(view):
atomic = any(db.get('ATOMIC_REQUESTS') for db in settings.DATABASES.values())
if atomic and inspect.iscoroutinefunction(view):
raise RuntimeError('ATOMIC_REQUESTS conflicts with an async view') Type guard
import inspect
from django.db import transaction
def view_is_async_and_atomic(view) -> bool:
aliases = set(getattr(view, '_non_atomic_requests', set()))
return inspect.iscoroutinefunction(view) and not aliases Prevention
- Avoid ATOMIC_REQUESTS in projects that use async views.
- Mark async views with @transaction.non_atomic_requests or set .non_atomic_requests.
- Add a system check that flags async views when ATOMIC_REQUESTS is enabled.
When it happens
Trigger: Setting 'ATOMIC_REQUESTS': True in DATABASES for a connection and defining any 'async def' view that uses (or is routed through) that connection; enabling ATOMIC_REQUESTS globally then adding a single async view.
Common situations: Enabling ATOMIC_REQUESTS to get per-request transactions then adopting async views; copying a sync config to an async-capable project without adjusting the setting.
Related errors
- Middleware %s must have at least one of sync_capable/async_c
- Deprecated email settings are not allowed when MAILERS is de
- Requested %s, but settings are not configured. You must eith
- The {name} setting is not available when MAILERS is defined.
- The SECRET_KEY setting must not be empty.
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/c344b0fdcd683b51.
Report an issue: GitHub.