chroma-core/chroma · error · TypeError

$abs requires a rank dict, got {type(child_data).__name__}

Error message

$abs requires a rank dict, got {type(child_data).__name__}

What it means

$abs is a unary rank operator that takes exactly one nested rank expression (e.g. abs of a difference) and expects its payload to be a single rank dict. Rank.from_dict raises this TypeError when the $abs payload is not a dict — most commonly a bare number or a list wrapping the operand.

Source

Thrown at chromadb/execution/expression/operator.py:814

            return result

        elif op == "$div":
            div_data = data["$div"]
            if not isinstance(div_data, dict):
                raise TypeError(
                    f"$div requires a dict with 'left' and 'right', got {type(div_data).__name__}"
                )
            if "left" not in div_data or "right" not in div_data:
                raise ValueError("$div requires 'left' and 'right' fields")

            left = Rank.from_dict(div_data["left"])
            right = Rank.from_dict(div_data["right"])
            return left / right

        elif op == "$abs":
            child_data = data["$abs"]
            if not isinstance(child_data, dict):
                raise TypeError(
                    f"$abs requires a rank dict, got {type(child_data).__name__}"
                )
            return abs(Rank.from_dict(child_data))

        elif op == "$exp":
            child_data = data["$exp"]
            if not isinstance(child_data, dict):
                raise TypeError(
                    f"$exp requires a rank dict, got {type(child_data).__name__}"
                )
            return Rank.from_dict(child_data).exp()

        elif op == "$log":
            child_data = data["$log"]
            if not isinstance(child_data, dict):
                raise TypeError(
                    f"$log requires a rank dict, got {type(child_data).__name__}"
                )

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Pass one unwrapped rank dict: {"$abs": {"$val": 5}} or {"$abs": {"$sub": {...}}}
  2. Wrap literals in $val: use {"$abs": {"$val": 5}} for abs of a constant
  3. Build in Python as abs(rank_expr).to_dict()
  4. Guard: isinstance(expr['$abs'], dict) before deserialization

Example fix

# before
expr = {"$abs": [{"$sub": {"left": a, "right": b}}]}

# after
expr = {"$abs": {"$sub": {"left": a, "right": b}}}
Defensive patterns

Strategy: validation

Validate before calling

def valid_abs_expr(expr):
    return (
        isinstance(expr, dict) and set(expr) == {"$abs"}
        and isinstance(expr["$abs"], dict)
    )

if not valid_abs_expr(rank_expr):
    raise ValueError(f"$abs payload must be a single rank dict: {rank_expr!r}")

Type guard

def is_abs_expr(d) -> bool:
    return (isinstance(d, dict) and set(d) == {"$abs"}
            and isinstance(d["$abs"], dict))

Try / catch

try:
    rank = Rank.from_dict(rank_expr)
except (TypeError, ValueError) as e:
    raise ValueError(f"invalid rank expression {rank_expr!r}: {e}") from e

Prevention

When it happens

Trigger: Rank.from_dict({'$abs': 5}), {'$abs': [{'$val': 5}]}, or {'$abs': [knn_rank]} — any non-dict payload, at any nesting depth in the expression tree.

Common situations: Wrapping the operand in brackets 'to be safe' because list operators ($sum/$mul) take lists; passing a literal number directly; hand-authored JSON that abbreviates {'$val': 5} to 5.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/982684555a51b03b. Report an issue: GitHub.