HKUDS/Vibe-Trading · error · ValueError

kind must be a string, got {type(value).__name__}

Error message

kind must be a string, got {type(value).__name__}

What it means

Raised by CashFlow normalize_kind when the kind argument is not a str instance (e.g. int, None, bytes). The kind label is normalized (lowercased, hyphens/spaces to underscores) and stored as a dataclass field, so non-string input fails fast in __post_init__ or the filter/external_flows helpers.

Source

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

def normalize_kind(value: str) -> str:
    """Normalize a cash-flow kind label to its canonical snake_case form.

    Real files spell the same concept as ``"Capital Call"``, ``"capital-call"``,
    or ``"CAPITAL_CALL"``. Normalizing means the sign check in ``KIND_DIRECTION``
    actually bites on those files instead of silently treating each spelling as
    an unconstrained custom kind.

    Args:
        value: Raw kind label from a caller or a file.

    Returns:
        Lower-cased label with spaces and hyphens collapsed to underscores.

    Raises:
        ValueError: If the label is not a string or is blank.
    """
    if not isinstance(value, str):
        raise ValueError(f"kind must be a string, got {type(value).__name__}")
    cleaned = value.strip().lower().replace("-", "_").replace(" ", "_")
    while "__" in cleaned:
        cleaned = cleaned.replace("__", "_")
    if not cleaned:
        raise ValueError("kind is required and cannot be empty")
    return cleaned


@dataclass(frozen=True)
class CashFlow:
    """A single dated cash amount in one currency.

    Attributes:
        date: Settlement date. ``datetime`` and ISO-8601 strings are accepted
            and normalized to ``datetime.date``.
        amount: Signed amount, positive into the holder. See the module
            docstring for the convention and its enforcement.
        kind: Canonical kind label, e.g. ``"capital_call"`` or ``"coupon"``.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Coerce kind to str before constructing: kind=str(value) if appropriate.
  2. Validate upstream data shapes (e.g. JSON schema) so kind is always a string.
  3. Check for None from optional mappings and supply a default label.

Example fix

# before
normalize_kind(123)

# after
normalize_kind(str(123))
Defensive patterns

Strategy: type-guard

Validate before calling

def kind_is_str(value) -> bool:
    return isinstance(value, str)

Type guard

def is_kind_string(value) -> bool:
    return isinstance(value, str)

Try / catch

try:
    flow = CashFlow(kind=raw_kind, ...)
except ValueError as e:
    if 'kind must be a string' in str(e):
        raise TypeError(f'kind must come from string fields, got {raw_kind!r}') from e
    raise

Prevention

When it happens

Trigger: Constructing a CashFlow-like entity or calling filter/external_flows with kind=42, kind=None, or bytes from un-decoded JSON input.

Common situations: Feeding raw JSON/YAML values where kind came back as a number or null; dynamic dicts built from spreadsheets where the column parses as int.

Related errors


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