HKUDS/Vibe-Trading · error · ValueError

legs[{index}].qty must be a non-zero integer

Error message

legs[{index}].qty must be a non-zero integer

What it means

Thrown when qty converts to float but is not a usable whole-number quantity: either it's a Python bool (True/False pass float() as 1.0/0.0) or it has a fractional part. Options trade in integer contract counts, so qty must be a non-zero integer value.

Source

Thrown at agent/src/tools/options_payoff_tool.py:247

    """Parse and validate raw JSON-style leg objects."""
    if not isinstance(raw, list) or not raw:
        raise ValueError("legs must be a non-empty array")
    if len(raw) > _MAX_LEGS:
        raise ValueError(f"legs may contain at most {_MAX_LEGS} entries")

    legs: list[OptionLeg] = []
    for index, item in enumerate(raw):
        if not isinstance(item, dict):
            raise ValueError(f"legs[{index}] must be an object")
        option_type = str(item.get("option_type") or "").strip().lower()
        try:
            strike = float(item["strike"])
            raw_qty = item["qty"]
            qty_number = float(raw_qty)
        except (KeyError, TypeError, ValueError, OverflowError) as exc:
            raise ValueError(f"legs[{index}] has invalid strike or qty: {exc}") from exc
        if isinstance(raw_qty, bool) or not qty_number.is_integer():
            raise ValueError(f"legs[{index}].qty must be a non-zero integer")
        qty = int(qty_number)
        raw_premium = item.get("premium")
        try:
            premium = None if raw_premium is None else float(raw_premium)
        except (TypeError, ValueError, OverflowError) as exc:
            raise ValueError(f"legs[{index}].premium must be numeric or null") from exc
        legs.append(OptionLeg(option_type, strike, qty, premium))
    return legs


def _required_float(kwargs: dict[str, Any], name: str) -> float:
    """Read a required finite float."""
    if name not in kwargs or kwargs[name] is None or kwargs[name] == "":
        raise ValueError(f"{name} is required")
    try:
        value = float(kwargs[name])
    except (TypeError, ValueError, OverflowError) as exc:
        raise ValueError(f"{name} must be numeric") from exc

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Round or reject fractional quantities before sending: qty=int(round(x)) only when semantically valid
  2. Never send booleans as qty
  3. Express ratios via separate legs with integer quantities or premium adjustments

Example fix

// before
{"option_type": "call", "strike": 100, "qty": 2.5}
// after
{"option_type": "call", "strike": 100, "qty": 3}  // round to whole contracts
Defensive patterns

Strategy: validation

Validate before calling

import math
for leg in legs:
    q = float(leg["qty"])
    assert not isinstance(leg["qty"], bool) and q.is_integer() and q != 0, leg

Type guard

def qty_is_valid(raw) -> bool:
    if isinstance(raw, bool): return False
    try: v = float(raw)
    except (TypeError, ValueError, OverflowError): return False
    return v.is_integer() and v != 0

Try / catch

try:
    execute(kwargs)
except ValueError as e:
    if "non-zero integer" in str(e):
        leg['qty'] = int(round(float(leg['qty'])))  # retry once if rounding acceptable

Prevention

When it happens

Trigger: qty=2.5, qty=0.5, qty=True, or qty=0-adjacent fractional values. Note float NaN also fails is_integer(). The message says non-zero, so qty=0.0 would also be rejected here.

Common situations: Position sizing calculators emitting fractional contracts; JSON auto-coercion of booleans; partial-share style APIs reused for options; users expressing ratio-weighted spreads.

Related errors


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