cocoindex-io/cocoindex · error · TypeError

expected tuple of length {expected_len}{loc}, got length {le

Error message

expected tuple of length {expected_len}{loc}, got length {len(value)}

What it means

For fixed-length tuple types, after confirming the value is a tuple, check_fixed_tuple verifies the arity matches the number of type arguments. This TypeError is raised when a tuple of the wrong length is supplied, e.g. a 3-element tuple for tuple[int, str]. The length must match exactly because each position has its own element checker.

Source

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

                    loc = f" at {path}" if path else ""
                    raise TypeError(f"expected tuple{loc}, got {type(value).__name__}")
                for i, elem in enumerate(value):
                    elem_fn(elem, f"{path}[{i}]")

            return check_var_tuple

        if args:
            # Fixed-length: tuple[X, Y, ...]
            elem_fns = [_build_check_fn(a) for a in args]
            expected_len = len(args)

            def check_fixed_tuple(value: Any, path: str) -> None:
                if not isinstance(value, tuple):
                    loc = f" at {path}" if path else ""
                    raise TypeError(f"expected {tp}{loc}, got {type(value).__name__}")
                if len(value) != expected_len:
                    loc = f" at {path}" if path else ""
                    raise TypeError(
                        f"expected tuple of length {expected_len}{loc}, "
                        f"got length {len(value)}"
                    )
                for i, (elem, fn) in enumerate(zip(value, elem_fns)):
                    fn(elem, f"{path}[{i}]")

            return check_fixed_tuple

        # Bare tuple (no args) — just check isinstance
        def check_bare_tuple(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__}")

        return check_bare_tuple

    # Simple concrete type: str, int, bytes, uuid.UUID, etc.
    if isinstance(tp, type):

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Fix the producer so it emits exactly the declared number of elements
  2. If arity is genuinely variable, switch the declared type to a dataclass, NamedTuple, or tuple[X, ...] as appropriate
  3. Log/print the value before the call to see which element count is being produced

Example fix

// before
return (user_id, name, email)  # declared tuple[int, str]
// after
return (user_id, name)
Defensive patterns

Strategy: validation

Validate before calling

if not (isinstance(value, tuple) and len(value) == expected_len):
    raise ValueError(f"need tuple of length {expected_len}, got {value!r}")

Type guard

def has_arity(value: object, n: int) -> TypeGuard[tuple]:
    return isinstance(value, tuple) and len(value) == n

Try / catch

try:
    check(value)
except TypeError as e:
    if "expected tuple of length" in str(e):
        log.error("arity mismatch: %s", e)
        raise

Prevention

When it happens

Trigger: Passing a tuple whose len() differs from the declared arity — e.g. (1, 'a', True) where tuple[int, str] is declared, or an empty tuple () for any non-empty fixed tuple type, via a @coco.fn argument, target-state field, or TypeChecker.check().

Common situations: Appending/removing a field from the declared type without updating producers; spreading optional values conditionally; building the tuple dynamically with variable item counts.

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/796b920578433775. Report an issue: GitHub.