deepset-ai / deepset-ai/haystack

fix: serialize_type / deserialize_type do not support typing.Annotated

Open
#12,338 1 comment 0 reactions 1 assignee View on GitHub

@davidsbatista is already working on this.

Since Aug 14, 2026.

P2
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.