Skip to content

🐛 Make FieldInfoMetadata hashable to fix Annotated type usage in sets#1889

Open
Yuerchu wants to merge 3 commits intofastapi:mainfrom
Yuerchu:fix/field-info-metadata-unhashable
Open

🐛 Make FieldInfoMetadata hashable to fix Annotated type usage in sets#1889
Yuerchu wants to merge 3 commits intofastapi:mainfrom
Yuerchu:fix/field-info-metadata-unhashable

Conversation

@Yuerchu
Copy link
Copy Markdown

@Yuerchu Yuerchu commented Apr 25, 2026

Description

FieldInfoMetadata is a plain @dataclass with implicit __eq__, which causes Python to set __hash__ to None. When FieldInfoMetadata instances are used as metadata inside typing.Annotated types, Annotated.__hash__ fails because it calls hash((origin, metadata_tuple)) and the FieldInfoMetadata element is unhashable.

This breaks FastAPI's OpenAPI schema generation in get_definitions(), which collects field annotations into a set:

input_types = {f.field_info.annotation for f in fields}
# TypeError: cannot use 'typing._AnnotatedAlias' as a set element (unhashable type: 'FieldInfoMetadata')

Fix

Add unsafe_hash=True to the @dataclass decorator. This is safe because FieldInfoMetadata instances are effectively immutable after construction — they are created once during model class definition and never mutated afterward.

Reproduction

Any project using custom Annotated field types with FieldInfoMetadata in the metadata (e.g. via sqlmodel_ext or similar libraries) will fail to generate /openapi.json with:

TypeError: cannot use 'typing._AnnotatedAlias' as a set element (unhashable type: 'FieldInfoMetadata')

🤖 Generated with Claude Code

Yuerchu and others added 2 commits April 26, 2026 00:30
…sets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@YuriiMotov
Copy link
Copy Markdown
Member

Could you please add the test that fails on main and gets fixed by this PR?

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Yuerchu
Copy link
Copy Markdown
Author

Yuerchu commented Apr 26, 2026

Added 3 test cases in tests/test_field_info_metadata_hashable.py:

  1. test_field_info_metadata_is_hashable — directly hashes a FieldInfoMetadata instance
  2. test_annotated_with_field_info_metadata_in_set — puts an Annotated type containing FieldInfoMetadata into a set
  3. test_annotated_field_type_in_set — realistic scenario with Annotated[int, Field(ge=0)] in a set

Tests 1 and 2 fail on main with TypeError: unhashable type: 'FieldInfoMetadata', and pass with this fix. Test 3 passes on both (since FieldInfo itself is already hashable) and is included as a regression guard.

@YuriiMotov
Copy link
Copy Markdown
Member

Not exactly what I meant)
These tests look artificial - you will not use it this way in your app, right?
We should try to come up with tests that describe realistic use case (model and how you would like to use it)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants