cocoindex-io/cocoindex · error · TypeError

memo_key transform for **kwargs must return dict, got {type(

Error message

memo_key transform for **kwargs must return dict, got {type(transformed).__name__}

What it means

A bug guard in the memo-key machinery, not user input validation: when a @coco.fn(memo=True) function has a varkw_override for **kwargs, _apply_memo_key applies that transform to the collected unmatched keyword arguments and expects it to yield a dict (the fingerprintable key material for the whole kwargs bag). A transform returning anything else would silently produce an unusable memo key, so this TypeError fires to surface a malformed user-supplied transform function at call time. Fix the varkw_override transform so it returns a dict.

Source

Thrown at python/cocoindex/_internal/function.py:512

    for key, value in kwargs.items():
        if key in memo_key_plan.keyword_specs:
            key_fn = memo_key_plan.keyword_specs[key]
            if key_fn is None:
                continue  # Exclude this kwarg
            new_kwargs[key] = key_fn(value)
        else:
            unmatched_kwargs[key] = value

    # Apply varkw override if present (whole **kwargs parameter)
    if not is_not_set(memo_key_plan.varkw_override):
        if memo_key_plan.varkw_override is None:
            # Exclude entire **kwargs
            unmatched_kwargs = {}
        else:
            # Transform entire unmatched kwargs dict
            transformed = memo_key_plan.varkw_override(unmatched_kwargs)
            if not isinstance(transformed, dict):
                raise TypeError(
                    f"memo_key transform for **kwargs must return dict, "
                    f"got {type(transformed).__name__}"
                )
            unmatched_kwargs = transformed

    # Merge matched and unmatched kwargs
    new_kwargs.update(unmatched_kwargs)

    return final_args, new_kwargs


def _normalize_memo_key(
    fn: Callable[..., Any], memo_key: MemoKeySpec
) -> PreparedMemoKeySpec | None:
    """Validate and compile per-parameter memo-key overrides once."""
    if memo_key is None:
        return None

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Return a plain dict from the kwargs transform: dict(transformed)
  2. If returning items pairs, wrap with dict(sorted(kwargs.items()))
  3. Ensure the transform handles empty kwargs by returning {}

Example fix

# before
def key(**kw):
    return sorted(kw.items())

# after
def key(**kw):
    return dict(sorted(kw.items()))
Defensive patterns

Strategy: validation

Validate before calling

def as_dict(result):
    if not isinstance(result, dict):
        raise TypeError("kwargs transform must return dict")
    return result

Type guard

def is_dict(x: object) -> TypeGuard[dict]:
    return isinstance(x, dict)

Prevention

When it happens

Trigger: @coco.fn with a memo_key transform for **kwargs (e.g. key "**" or varkw override) whose callable returns a list of pairs, None, or a non-dict mapping-like object, for a function accepting **kwargs.

Common situations: Returning sorted(kwargs.items()) (a list); returning a custom Mapping type; forgetting dict() around a filtered comprehension.

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/938963ed5b8cf523. Report an issue: GitHub.