cocoindex-io/cocoindex · error · TypeError

expected None{loc}, got {type(value).__name__}

Error message

expected None{loc}, got {type(value).__name__}

What it means

TypeChecker builds a validator per type annotation. For the NoneType annotation (type(None) / None in a Union), the generated check_none closure raises TypeError when the value is not None, with an optional `at <path>` location suffix. This enforces that a None-typed slot only ever holds None.

Source

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

def _build_check_fn(tp: Any) -> _CheckFn:
    """
    Build a validation closure for the given type annotation.

    Returns a function ``(value, path) -> None`` that raises ``TypeError``
    on mismatch.  *path* carries positional context for error messages
    (empty string at top level, ``"[0]"`` for tuple element 0, etc.).
    """
    origin = typing.get_origin(tp)
    args = typing.get_args(tp)

    # NoneType
    if tp is type(None):

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

        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

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Fix the annotation to the actual intended type instead of None
  2. Make the value actually None at the call site
  3. If both None and a real type are valid, annotate as `T | None`

Example fix

// before
def cb() -> None:
    return result  # returns a value

// after
def cb() -> Result:
    return result
# or return None if the declared type is correct
Defensive patterns

Strategy: type-guard

Validate before calling

if value is not None:
    raise TypeError("slot annotated None received a non-None value")

Type guard

def is_none(v: object) -> TypeGuard[None]:
    return v is None

Try / catch

try:
    checker.check(value)
except TypeError as e:
    if "expected None" in str(e):
        value = None
    else:
        raise

Prevention

When it happens

Trigger: A value typed as None (or the None member of a union being checked individually by check_union) receives a non-None value at runtime — e.g. a memoized function declared to return None actually returns something, or a tuple element typed None gets a real value.

Common situations: Accidentally annotating a field with `None` instead of the intended type; returning a sentinel value from a function declared -> None; misuse of None as a placeholder type.

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/4c94edf64ebac497. Report an issue: GitHub.