Skip to content

@limiter.limit breaks FastAPI routes using string annotations + Annotated Depends when no explicit request param is present #247

@adshz

Description

@adshz

Summary

When applying @limiter.limit to a FastAPI route that uses from __future__ import annotations and Annotated[..., Depends(...)],
the decorator breaks dependency resolution if the endpoint does not
explicitly declare a request: Request parameter. The route either
fails at import time (“No request or websocket argument”) or returns
422 responses because FastAPI sees an altered signature.

Minimal reproduction

from __future__ import annotations
from typing import Annotated

from fastapi import Depends, FastAPI
from slowapi import Limiter
from slowapi.util import get_remote_address
from starlette.requests import Request

app = FastAPI()
limiter = Limiter(key_func=get_remote_address)

async def dep(request: Request) -> str:
    return "ok"

@app.get("/demo")
@limiter.limit("5/minute")
async def demo_endpoint(value: Annotated[str, Depends(dep)]) -> str:
    return value

Steps:

  1. Run the app with FastAPI/uvicorn (FastAPI 0.119.1, SlowAPI 0.1.9,
    Python 3.12).
  2. Observe either:
    • Import-time error: Exception: No "request" or "websocket"
      argument on function "<function demo_endpoint ...>"
    • Or 422 responses when calling /demo because FastAPI cannot
      resolve dependencies.

Expected

  • The decorator should work with FastAPI’s annotation styles (from
    future import annotations, Annotated[Depends(...)]) even when
    the endpoint doesn’t declare request explicitly.
  • Requests should succeed until the limit is exceeded, then return
    429 with rate-limit headers.

Actual

  • Import-time crash (“No request or websocket argument”) or 422
    responses due to a mutated signature that FastAPI can’t resolve.

Environment

  • Python: 3.12
  • FastAPI: 0.119.1
  • SlowAPI: 0.1.9 (PyPI)
  • OS: macOS / Linux (reproducible in both)

Notes / possible fix direction

  • The decorator currently wraps the endpoint and requires a request
    param, altering the signature FastAPI inspects.
  • Preserving the original callable’s signature (or rebuilding
    it correctly) and setting wrapped, signature,
    annotations, and globals appears necessary for FastAPI’s
    dependency resolver.
  • Avoiding the decorator wrapper and registering limits directly
    (using LimitGroup + limiter._route_limits) works around the issue,
    but an upstream fix would let users keep the decorator.

Workaround

  • Register limits manually and enforce via a dependency that calls
    _check_request_limit + _inject_headers, keeping the endpoint
    signature untouched.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions