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

  1. Disable ATOMIC_REQUESTS in DATABASES, or set it to False for connections used by async views.
  2. 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.
  3. 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

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


AI-assisted analysis of django/django@ae25a40be0 (2026-08-06). Data as JSON: /api/errors/c344b0fdcd683b51. Report an issue: GitHub.