deepset-ai / deepset-ai/haystack
fix: serialize_type / deserialize_type do not support typing.Annotated
@davidsbatista is already working on this.
Since Aug 14, 2026.
- Dominant language
- Python
- Stars
- 26.6k
- Forks
- 3.2k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 194
Description
Describe the bug
typing.Annotated types cannot be round-tripped through serialize_type / deserialize_type. serialize_type renders the metadata values as if they were type names, and deserialize_type then fails to load them. A pipeline using an Annotated type saves without error but crashes on load.
This is the same shape as #12285 (now fixed for typing.Literal) — the underlying code path that handles Literal doesn't handle Annotated, and the metadata values are not types.
To Reproduce
from typing import Annotated
from haystack.utils.type_serialization import serialize_type, deserialize_type
s = serialize_type(Annotated[int, "doc"])
print(s) # typing.Annotated[int, doc]
deserialize_type(s) # raises
typing.Annotated[int, doc]
haystack.core.errors.DeserializationError: Could not deserialize type: doc
Silent-corruption variant: Annotated[str, "int"] round-trips to Annotated[str, int] (the string "int" is silently read as the int type) with no error.
Expected behavior
serialize_type / deserialize_type should round-trip typing.Annotated, preserving both the wrapped type and the metadata values (string, int, bool, None, bytes — same kinds Python's own Annotated accepts as literal metadata).
Use case
Annotated is the standard way to attach documentation / field metadata in modern Python typing. Pydantic's Field(description=...), FastAPI's Query(...), and several Haystack tool-parameter schemas all rely on it. A component that exposes a Callable returning an Annotated type — e.g. an OutputAdapter whose output type is Annotated[Result, "computed"] — currently cannot be Pipeline.dumps()'d and reloaded.
Proposed fix
Mirror the Literal fix from PR #12286: on serialize, render each metadata value with repr() so strings keep their quotes; on deserialize, use ast.literal_eval to parse metadata values safely (quote-aware, limited to safe literals). The first argument is the wrapped type, which is resolved via the existing deserialize_type path. A quote-aware arg splitter is needed because the metadata can contain a comma (e.g. Annotated[int, "a, b"]).
Scope note: this covers the literal metadata kinds Python's own Annotated accepts and that survive a text round-trip (str, bytes, int, bool, None). Type metadata (e.g. Annotated[int, SomeValidator]) is also handled via a fallback to deserialize_type for non-literal metadata. Enum members and arbitrary callables are out of scope — they cannot be reconstructed from text alone.
System:
- Haystack version: 3.1.0rc0 (main)
- Branch: main @ ba92ec9de
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.