Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix ForwardRef resolution inside Annotated in get_typed_annotation#15481

Draft
siddharth10ss wants to merge 2 commits into
fastapi:masterfrom
siddharth10ss:fix/forward-ref-in-annotated
Draft

Fix ForwardRef resolution inside Annotated in get_typed_annotation#15481
siddharth10ss wants to merge 2 commits into
fastapi:masterfrom
siddharth10ss:fix/forward-ref-in-annotated

Conversation

@siddharth10ss
Copy link
Copy Markdown

Fix ForwardRef resolution inside Annotated (Closes #13056)

Problem

When using from __future__ import annotations, type annotations are stored as strings (ForwardRef). FastAPI resolves forward references when the annotation itself is a string, but fails when the ForwardRef is wrapped inside typing.Annotated.

Example:

from __future__ import annotations
from typing import Annotated
from fastapi import Depends

@app.get("/")
async def read_root(potato: Annotated["Potato", Depends(get_potato)]):
    ...

In this case, "Potato" is not resolved correctly, which leads to incorrect dependency parsing and incorrect OpenAPI schema generation.


Root Cause

get_typed_annotation() only resolves forward references when the annotation is directly a str or ForwardRef. It does not handle ForwardRefs nested inside Annotated.


Solution

  • Detect Annotated types using get_origin
  • Extract base type and metadata via get_args
  • Resolve ForwardRef for the base type (handles both str and ForwardRef)
  • Recursively resolve nested annotations
  • Reconstruct Annotated using __class_getitem__ to preserve correct structure

Changes

  • Added ForwardRef resolution for base types inside Annotated
  • Added recursive handling for nested Annotated cases
  • Preserved existing behavior for non-Annotated types
  • Ensured consistent handling of NoneType

Tests

Added test to validate:

  • Annotated[Potato, Depends(...)] (existing behavior unchanged)
  • Annotated["Potato", Depends(...)] (ForwardRef correctly resolved)
  • OpenAPI schema generation does not leak unresolved ForwardRef strings

Notes

  • Change is minimal and localized to get_typed_annotation()
  • No breaking changes introduced
  • Compatible with existing type resolution flow

Closes #13056

Copilot AI review requested due to automatic review settings May 4, 2026 16:02
Comment on lines +42 to +56
def test_annotated_forwardref_response() -> None:
"""Endpoint using Annotated[ForwardRef, Depends(...)] must return 200."""
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"color": "red", "size": 10}


def test_annotated_forwardref_openapi_schema() -> None:
"""OpenAPI schema must be generated without errors and must not leak ForwardRef."""
response = client.get("/openapi.json")
assert response.status_code == 200
schema_text = response.text
assert "ForwardRef" not in schema_text, (
"Unresolved ForwardRef leaked into the OpenAPI schema"
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests pass on master

@YuriiMotov YuriiMotov marked this pull request as draft May 4, 2026 18:49
@YuriiMotov
Copy link
Copy Markdown
Member

Let's keep it as draft until CI passes.
Also, please, review other existing PRs linked to the issue to ensure your approach is not a duplicate of existing PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't use Annotated with ForwardRef

2 participants