How It Works Features Pricing Blog Error Guides
Log In Start Free Trial
FastAPI · Python

Fix RuntimeError: RuntimeError: No response returned from ASGI application in FastAPI

This error occurs when custom middleware does not properly call the next middleware or return a response, breaking the ASGI chain. Fix it by ensuring your middleware calls call_next(request) and returns the response object. If using BaseHTTPMiddleware, the dispatch method must return the response from call_next.

Reading the Stack Trace

Traceback (most recent call last): File "/app/venv/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 406, in run_asgi result = await app(scope, receive, send) File "/app/venv/lib/python3.11/site-packages/starlette/middleware/__init__.py", line 24, in __call__ await self.app(scope, receive, send) File "/app/venv/lib/python3.11/site-packages/starlette/middleware/base.py", line 68, in __call__ response = await self.dispatch(request, call_next) File "/app/src/middleware/logging_middleware.py", line 15, in dispatch logger.info(f"Request: {request.method} {request.url}") RuntimeError: No response returned from ASGI application

Here's what each line means:

Common Causes

1. Not calling call_next in dispatch

The middleware dispatch method performs some logic but forgets to call call_next(request) to pass the request to the next handler.

from starlette.middleware.base import BaseHTTPMiddleware
import logging

logger = logging.getLogger(__name__)

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        logger.info(f"Request: {request.method} {request.url}")
        # Missing: response = await call_next(request)
        # Missing: return response

2. Not returning the response from dispatch

The middleware calls call_next but does not return the response, so Starlette receives None.

class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        start = time.time()
        response = await call_next(request)
        duration = time.time() - start
        logger.info(f"Duration: {duration:.3f}s")
        # Missing: return response

3. Exception in middleware before call_next

The middleware raises an exception before reaching call_next, and there is no error handling to return a proper response.

class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        api_key = request.headers["X-API-Key"]  # KeyError if missing
        if not validate_key(api_key):
            raise ValueError("Invalid key")
        response = await call_next(request)
        return response

The Fix

Ensure the dispatch method always calls await call_next(request) to pass the request down the middleware chain and always returns the resulting Response object. Adding a return type annotation helps catch this mistake during development.

Before (broken)
from starlette.middleware.base import BaseHTTPMiddleware
import logging

logger = logging.getLogger(__name__)

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        logger.info(f"Request: {request.method} {request.url}")
After (fixed)
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
import logging
import time

logger = logging.getLogger(__name__)

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next) -> Response:
        start = time.time()
        logger.info(f"Request: {request.method} {request.url}")
        response = await call_next(request)
        duration = time.time() - start
        response.headers["X-Process-Time"] = f"{duration:.3f}"
        logger.info(f"Response: {response.status_code} in {duration:.3f}s")
        return response

Testing the Fix

import pytest
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)


def test_middleware_returns_response():
    response = client.get("/health")
    assert response.status_code == 200


def test_middleware_adds_process_time_header():
    response = client.get("/health")
    assert "x-process-time" in response.headers
    assert float(response.headers["x-process-time"]) >= 0


def test_middleware_does_not_break_post_requests():
    response = client.post("/api/data", json={"key": "value"})
    assert response.status_code in (200, 201, 422)
    assert "x-process-time" in response.headers


def test_middleware_handles_404():
    response = client.get("/nonexistent")
    assert response.status_code == 404
    assert "x-process-time" in response.headers

Run your tests:

pytest tests/test_middleware.py -v

Pushing Through CI/CD

git checkout -b fix/fastapi-middleware,git add src/middleware/logging_middleware.py tests/test_middleware.py,git commit -m "fix: add call_next and return response in logging middleware dispatch",git push origin fix/fastapi-middleware

Your CI config should look something like this:

name: CI
on:
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
      - run: pip install -r requirements.txt
      - run: pytest --tb=short -q

The Full Manual Process: 18 Steps

Here's every step you just went through to fix this one bug:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. Monitor production to confirm the error is resolved

Total time: 30-60 minutes. For one bug.

Or Let bugstack Fix It in Under 2 minutes

Every step above? bugstack does it automatically.

Step 1: Install the SDK

pip install bugstack

Step 2: Initialize

import bugstack

bugstack.init(api_key=os.environ["BUGSTACK_API_KEY"])

Step 3: There is no step 3.

bugstack handles everything from here:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. Deploys to production (or opens a PR for review)

Time from error to fix deployed: Under 2 minutes.

Human involvement: zero.

Try bugstack Free →

No credit card. 5-minute setup. Cancel anytime.

Deploying the Fix (Manual Path)

  1. Run the test suite locally to confirm middleware passes requests through correctly.
  2. Open a pull request with the middleware fix.
  3. Wait for CI checks to pass on the PR.
  4. Have a teammate review and approve the PR.
  5. Merge to main and verify all endpoints respond correctly in staging.

Frequently Asked Questions

BugStack tests that all endpoints return valid responses through the middleware, checks for the expected custom headers, and runs your full test suite to confirm no requests are dropped.

BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review the middleware changes before merging.

BaseHTTPMiddleware is easier to write but has limitations with streaming responses. For advanced use cases like streaming or WebSocket handling, implement pure ASGI middleware instead.

Middleware executes in the order it is added with app.add_middleware(). The last added middleware runs first for incoming requests and last for outgoing responses, like a stack.