cocoindex-io/cocoindex · error · TypeError

expected {tp}{loc}, got {type(value).__name__}: {value!r}

Error message

expected {tp}{loc}, got {type(value).__name__}: {value!r}

What it means

For a union annotation, TypeChecker tries each member's validator and only accepts the value if one succeeds. If every member raises TypeError, check_union raises a final TypeError naming the full union type, the value's runtime type, and a repr of the value, with an optional path suffix for nested positions.

Source

Thrown at python/cocoindex/_internal/datatype.py:321

        return check_none

    # Any — accept everything
    if tp is Any:
        return lambda _v, _p: None

    # Union: str | int, str | None, etc.
    if origin in (types.UnionType, typing.Union):
        sub_fns = [_build_check_fn(a) for a in args]

        def check_union(value: Any, path: str) -> None:
            for fn in sub_fns:
                try:
                    fn(value, path)
                    return
                except TypeError:
                    continue
            loc = f" at {path}" if path else ""
            raise TypeError(
                f"expected {tp}{loc}, got {type(value).__name__}: {value!r}"
            )

        return check_union

    # Tuple types
    if origin is tuple:
        if len(args) == 2 and args[1] is Ellipsis:
            # Variable-length: tuple[X, ...]
            if args[0] is Any:

                def check_var_tuple_any(value: Any, path: str) -> None:
                    if not isinstance(value, tuple):
                        loc = f" at {path}" if path else ""
                        raise TypeError(
                            f"expected tuple{loc}, got {type(value).__name__}"
                        )

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Coerce the value to one of the union members before passing it (str(x), int(x))
  2. Widen the annotation to include the actual runtime type
  3. Fix the producer so it emits the declared type

Example fix

// before
key = (table, 3.5)  # annotated tuple[str, str | int]

// after
key = (table, str(3.5))
Defensive patterns

Strategy: validation

Validate before calling

def matches_union(v, members) -> bool:
    return any(isinstance(v, m) for m in members)

Type guard

def is_str_or_int(v: object) -> TypeGuard[str | int]:
    return isinstance(v, (str, int))

Try / catch

try:
    checker.check(value)
except TypeError as e:
    logging.error("union mismatch: %s", e)
    value = coerce(value)  # str(value) or int(value)

Prevention

When it happens

Trigger: Passing a value that matches none of the union members — e.g. a key/argument annotated `str | int` receiving bytes, or `str | None` receiving a list — wherever TypeChecker validates stable keys or memo arguments.

Common situations: Config values read from JSON/yaml arriving as the wrong scalar type (e.g. int vs str); bytes-vs-str mixups; nested tuple element failing a `str | int` annotation (path like "[2]").

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/9150e99e1743aa31. Report an issue: GitHub.