From dcce6ecd915687d47226b2260cbed0dc366d29ba Mon Sep 17 00:00:00 2001 From: Ofek Danny <63648262+OfekDanny@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:00:36 +0300 Subject: [PATCH] fix: resolve ForwardRef inside Annotated in get_typed_annotation When a type annotation uses a string literal inside Annotated, e.g. Annotated['Potato', Depends(get_potato)], Python stores it as Annotated[ForwardRef('Potato'), Depends(get_potato)] at runtime. get_typed_annotation only handled the case where the annotation itself is a string/ForwardRef. It did not recurse into Annotated's first argument. As a result, the ForwardRef was never resolved, FastAPI could not identify the type, and the parameter was treated as Any, producing an incorrect OpenAPI schema. Fix by also handling the case where annotation is ForwardRef directly (not wrapped in a string), and by recursively resolving any ForwardRef found as the first argument of an Annotated type. Fixes #13056 --- fastapi/dependencies/utils.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index aceca6a1d3..2f7a35c9e3 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -245,9 +245,22 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature: def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any: if isinstance(annotation, str): annotation = ForwardRef(annotation) - annotation = evaluate_forwardref(annotation, globalns, globalns) + if isinstance(annotation, ForwardRef): + annotation = evaluate_forwardref(annotation, globalns, globalns) # ty: ignore[deprecated] if annotation is type(None): return None + # Resolve any ForwardRef inside the first argument of Annotated. + # Python automatically wraps string args in ForwardRef, e.g.: + # Annotated["Potato", Depends(get_potato)] + # becomes Annotated[ForwardRef("Potato"), Depends(get_potato)] at runtime. + # Without this, FastAPI never sees the resolved type and treats the + # parameter as Any, producing an incorrect OpenAPI schema. + if get_origin(annotation) is Annotated: + args = get_args(annotation) + first_arg = args[0] + if isinstance(first_arg, (str, ForwardRef)): + resolved_first = get_typed_annotation(first_arg, globalns) + annotation = Annotated[tuple([resolved_first] + list(args[1:]))] return annotation