HKUDS/Vibe-Trading · error · ValueError

metadata must be a mapping, got {type(self.metadata).__name_

Error message

metadata must be a mapping, got {type(self.metadata).__name__}

What it means

CashFlow.metadata must be any Mapping (dict, MappingProxy, etc.); anything else (list, string, None) is rejected. The entity then freezes it into an immutable MappingProxyType to guarantee flows are hashable-ish and tamper-proof.

Source

Thrown at agent/src/entities/cashflow.py:181

            raise ValueError(
                f"amount must be a finite number, got {self.amount!r}; a missing "
                "value must be fixed at the source, not carried as NaN"
            )
        object.__setattr__(self, "amount", amount)

        required_sign = KIND_DIRECTION.get(self.kind)
        if required_sign is not None and amount != 0.0:
            if (amount > 0) != (required_sign > 0):
                direction = "positive (cash in)" if required_sign > 0 else "negative (cash out)"
                raise ValueError(
                    f"kind={self.kind!r} must have a {direction} amount under the "
                    f"holder-perspective sign convention, got {amount!r}. Flip the "
                    "sign, or use a distinct kind if this flow is genuinely "
                    "two-directional (e.g. 'recallable_distribution')."
                )

        if not isinstance(self.metadata, Mapping):
            raise ValueError(
                f"metadata must be a mapping, got {type(self.metadata).__name__}"
            )
        object.__setattr__(self, "metadata", MappingProxyType(dict(self.metadata)))

    @property
    def is_valuation(self) -> bool:
        """Whether this record is a mark rather than a settled cash movement.

        Returns:
            True when ``kind`` is one of ``VALUATION_KINDS``.
        """
        return self.kind in VALUATION_KINDS


@dataclass(frozen=True)
class CashFlowSeries:
    """An ordered, immutable collection of ``CashFlow`` in one currency.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Pass a dict (or omit metadata entirely, which uses the default empty mapping)
  2. If metadata comes from external data, coerce non-mappings or reject them at ingest

Example fix

# before
CashFlow(..., metadata='imported')
# after
CashFlow(..., metadata={'source': 'imported'})
Defensive patterns

Strategy: type-guard

Validate before calling

metadata = row.get('metadata') if isinstance(row.get('metadata'), Mapping) else {}

Type guard

from collections.abc import Mapping
def is_valid_metadata(v) -> bool:
    return isinstance(v, Mapping)

Try / catch

try:
    CashFlow(..., metadata=md)
except ValueError as e:
    if 'metadata must be a mapping' in str(e):
        md = {'value': md}  # coerce or drop

Prevention

When it happens

Trigger: CashFlow(..., metadata=['tag1','tag2']) or metadata=None or metadata='note' — any non-Mapping value.

Common situations: Passing JSON-loaded values where metadata arrived as a list; a default of None from an optional parser branch; forgetting the field expects key-value pairs.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/3f48b85c55dc6d84. Report an issue: GitHub.