HKUDS/Vibe-Trading · error · ValueError

flow_timing must be {FLOW_TIMING_END!r} or {FLOW_TIMING_STAR

Error message

flow_timing must be {FLOW_TIMING_END!r} or {FLOW_TIMING_START!r}, got {raw!r}

What it means

The `flow_timing` option must be exactly one of the two recognized tokens (end or start), after stripping and lowercasing. Any other non-empty value is rejected because cashflow timing conventions change computed results (end-of-period vs start-of-period discounting).

Source

Thrown at agent/src/tools/cashflow_analytics_tool.py:420

def _coerce_flow_timing(raw: Any) -> str:
    """Validate the flow-timing token.

    Args:
        raw: Value supplied for ``flow_timing``; ``None`` selects the default.

    Returns:
        Either :data:`~src.quantlib.performance.FLOW_TIMING_END` or
        :data:`~src.quantlib.performance.FLOW_TIMING_START`.

    Raises:
        ValueError: If the token is not one of the two recognised values.
    """
    if raw is None or raw == "":
        return FLOW_TIMING_END
    token = str(raw).strip().lower()
    if token not in (FLOW_TIMING_END, FLOW_TIMING_START):
        raise ValueError(
            f"flow_timing must be {FLOW_TIMING_END!r} or {FLOW_TIMING_START!r}, "
            f"got {raw!r}"
        )
    return token


def _coerce_kinds(raw: Any, field_name: str) -> list[str] | None:
    """Validate an optional kind-classification override.

    Args:
        raw: Value supplied for ``external_kinds`` or ``internal_kinds``.
        field_name: Name used in the error message.

    Returns:
        The list of labels, or ``None`` to keep the module default.

    Raises:
        ValueError: If the value is not an array of non-empty strings.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Set flow_timing to 'end' or 'start' (case-insensitive), or omit it / pass '' to default to 'end'
  2. If the value comes from user config, validate it against the allowed set before invoking the tool

Example fix

# before
result = execute(flows=flows, flow_timing="beginning")
# after
result = execute(flows=flows, flow_timing="start")  # 'beginning' maps to 'start'
Defensive patterns

Strategy: type-guard

Validate before calling

ALLOWED = {"end", "start"}
def norm_timing(v):
    if v is None:
        return "end"
    t = str(v).strip().lower()
    assert t in ALLOWED, f"flow_timing must be one of {ALLOWED}, got {v!r}"
    return t

Type guard

def is_flow_timing(v) -> bool:
    return v is None or (isinstance(v, str) and (not v.strip() or v.strip().lower() in {"end", "start"}))

Try / catch

try:
    execute(flows=flows, flow_timing=timing)
except ValueError as exc:
    if "flow_timing must be" in str(exc):
        execute(flows=flows, flow_timing="end")  # safe default
    else:
        raise

Prevention

When it happens

Trigger: Passing flow_timing='beginning', 'END ' (ok, lowercased), 'middle', 'e', or an arbitrary string to the tool's execute(); only ''/None and the two exact tokens (case-insensitive) are accepted.

Common situations: Callers copy terminology from other finance libraries (e.g. numpy-financial's 'beginning'/'end'); LLM agents guess the enum; config files carry a stale token from an older version.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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