Julius Faber
08/11/2025, 10:03 AMJulius Faber
08/11/2025, 10:07 AMMarvin
08/11/2025, 10:07 AMMarvin
08/11/2025, 10:07 AMMarvin
08/11/2025, 10:11 AMfrom typing import TypeVar, Generic
T = TypeVar("T", default=int) # has a default
R = TypeVar("R") # no default
class Pair(Generic[T, R]): # BAD: non-default after default
pass
- Using the new 3.12 type parameter syntax (PEP 695):
def f<x: T|T=int, R> -> R: # BAD
...
How to fix it
- Put all non-default type parameters first, then the ones with defaults; or give defaults to all following parameters; or remove the defaults entirely.
# Classic API
from typing import TypeVar, Generic
R = TypeVar("R") # no default first
T = TypeVar("T", default=int) # defaulted after
class Pair(Generic[R, T]): # GOOD
pass
# New 3.12 syntax
def f<x: T|R, T=int> -> R: # GOOD
...
How to confirm your environment
- Check versions (this often explains the “sometimes”):
python -c "import sys, typing_extensions as te; print(sys.version); print(getattr(te, '__version__', 'no typing_extensions'))"
If you’re on Python 3.12+ or using a recent typing_extensions (backporting PEP 696), the rule will be enforced.
Authoritative references
- PEP 695 (Type Parameter Syntax): https://peps.python.org/pep-0695/
- PEP 696 (Type defaults for TypeVarLikes): https://peps.python.org/pep-0696/
- typing docs for TypeVar defaults (3.12+): https://docs.python.org/3/library/typing.html#typing.TypeVar
- typing_extensions changelog (backports of these features): https://github.com/python/typing_extensions/blob/main/CHANGELOG.md
If you can share:
- The snippet where the generic is declared
- Your Python version and typing_extensions version
I can point at the exact parameter ordering that’s causing the fuss and suggest the minimal change.Marvin
08/11/2025, 10:12 AMpython - <<'PY'
import sys
import typing_extensions as te
print("Python:", sys.version)
print("typing_extensions:", getattr(te, "__version__", "unknown"), te.__file__)
try:
import pip._vendor.typing_extensions as vte
print("pip vendored typing_extensions:", vte.__file__)
print("Same module object?", te is vte)
except Exception as e:
print("pip vendored not importable:", e)
PY
If both are importable and they’re not the same object, you’ve found the culprit.
2) Upgrade to a single, current typing_extensions and pip
pip install -U pip typing-extensions
Then re‑run the probe. Ideally, only one copy is ever imported during your run.
3) Make sure no dependency is importing pip._vendor.typing_extensions
- Grep your codebase and known deps for that import path. If a library does this, upgrade it. If it’s pinned and you’re stuck, consider reporting upstream; this import is brittle for exactly this reason.
4) Double‑check you’re not declaring a Generic with defaults in the wrong order
If you have anything like:
from typing import Generic, TypeVar
T = TypeVar("T", default=int)
R = TypeVar("R")
class Bad(Generic[T, R]): # will raise
pass
Fix by moving defaults to the end:
class Good(Generic[R, T]):
pass
This won’t explain the intermittency, but it’s worth ruling out.
5) If you must ship today and can’t untangle the imports
A pragmatic (temporary) workaround some folks use is to ensure the same NoDefault sentinel is used across modules by forcing the non‑vendored typing_extensions to be imported first and, if necessary, avoiding libraries that import pip._vendor.typing_extensions. I wouldn’t recommend monkey‑patching in production unless you’re desperate.
Why Prefect shows up in the stack:
- Prefect’s call_with_parameters
is just where your function call passes through; it triggers the subscript/validation of some generic in your environment. Prefect 3.x itself isn’t the source of this error.
If you share:
- Your Prefect, pydantic, typing-extensions versions
- The output of the probe above
- Any custom Generic/TypeVar definitions you have
…I can help pinpoint the exact offender and suggest a precise pin/upgrade.