HKUDS/Vibe-Trading · error · ValueError

{label} must be non-negative, got {value!r}

Error message

{label} must be non-negative, got {value!r}

What it means

waterfall_split requires distributable, contributed_capital, and preferred_amount to be non-negative; these are dollar magnitudes and a negative value would break every downstream tier calculation.

Source

Thrown at agent/src/quantlib/fundmath.py:1173

        catch_up_rate: GP share of each catch-up dollar, in ``[0, 1]``. Pass
            ``0.0`` for a fund with no catch-up tier; any other value must
            exceed ``carry_rate``, or the tier could never complete.

    Returns:
        A :class:`WaterfallResult` whose tiers sum exactly to ``distributable``.

    Raises:
        ValueError: If any amount is negative, if ``carry_rate`` is outside
            ``[0, 1)``, if ``catch_up_rate`` is outside ``[0, 1]``, or if a
            non-zero ``catch_up_rate`` does not exceed ``carry_rate``.
    """
    for label, value in (
        ("distributable", distributable),
        ("contributed_capital", contributed_capital),
        ("preferred_amount", preferred_amount),
    ):
        if value < 0.0:
            raise ValueError(f"{label} must be non-negative, got {value!r}")
    if not 0.0 <= carry_rate < 1.0:
        raise ValueError(f"carry_rate must be in [0, 1), got {carry_rate!r}")
    if not 0.0 <= catch_up_rate <= 1.0:
        raise ValueError(f"catch_up_rate must be in [0, 1], got {catch_up_rate!r}")
    if catch_up_rate > 0.0 and catch_up_rate <= carry_rate:
        raise ValueError(
            f"catch_up_rate={catch_up_rate!r} must exceed carry_rate="
            f"{carry_rate!r}, otherwise the catch-up tier can never complete. "
            "Pass catch_up_rate=0.0 for a fund with no catch-up."
        )

    remaining = float(distributable)

    return_of_capital = min(remaining, float(contributed_capital))
    remaining -= return_of_capital

    preferred_paid = min(remaining, float(preferred_amount))
    remaining -= preferred_paid

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Verify the sign convention: all three arguments are non-negative dollar amounts
  2. Clamp tiny negative floats from floating-point drift: max(0.0, value)
  3. Trace where the negative number originated (accrual, aggregation, or parse) and fix it

Example fix

# before
waterfall_split(distributable=dist, contributed_capital=paid, preferred_amount=pref)  # pref=-1e-12

# after
waterfall_split(
    distributable=max(0.0, dist),
    contributed_capital=max(0.0, paid),
    preferred_amount=max(0.0, pref),
)
Defensive patterns

Strategy: validation

Validate before calling

distributable = max(0.0, distributable)
contributed_capital = max(0.0, contributed_capital)
preferred_amount = max(0.0, preferred_amount)

Prevention

When it happens

Trigger: Calling waterfall_split(distributable=-100.0, ...) or passing a negative preferred_amount computed by an accrual bug, via european_waterfall/_pooled_entitlement or directly in tests.

Common situations: Sign-convention mix-ups (treating distributions as negative); a preferred-return accrual that went negative; feeding gross instead of net values with offsets applied twice.

Related errors


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