HKUDS/Vibe-Trading · error · ValueError

legs must be a non-empty array

Error message

legs must be a non-empty array

What it means

Thrown by _coerce_legs when the `legs` argument to the options payoff tool is not a JSON array or is an empty array. Legs define the option positions for payoff/greeks computation, so at least one leg is structurally required. This is the first schema gate before per-leg validation runs.

Source

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

            },
            "scenario_grid": {
                "iv_values": _rounded_array(iv_values),
                "spot": _rounded_array(report.spot_grid),
                "pnl": [_rounded_array(row) for row in np.asarray(scenarios, dtype=float)],
            },
            "limitations": [
                "European Black-Scholes marks with constant rate and volatility per scenario.",
                "No dividends, early exercise, assignment, slippage, or margin model.",
                "Scenario P&L is mark-to-market and does not deduct a hypothetical exit commission.",
            ],
        }
        return json.dumps(payload, ensure_ascii=False, allow_nan=False)


def _coerce_legs(raw: Any) -> list[OptionLeg]:
    """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")

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Pass legs as a parsed JSON array with at least one leg object, e.g. [{"option_type":"call","strike":100,"qty":1}]
  2. If legs arrives as a JSON string, json.loads it before calling the tool
  3. Reject empty portfolios upstream in the calling agent's prompt/schema

Example fix

// before
result = execute({"legs": [], "entry_spot": 100})
// after
result = execute({"legs": [{"option_type": "call", "strike": 100, "qty": 1, "premium": 2.5}], "entry_spot": 100})
Defensive patterns

Strategy: validation

Validate before calling

import json
if isinstance(legs, str):
    legs = json.loads(legs)
if not isinstance(legs, list) or not legs:
    raise ValueError("legs must be a non-empty array of leg objects")

Type guard

def is_legs_input(raw) -> bool:
    return isinstance(raw, list) and len(raw) > 0 and all(isinstance(x, dict) for x in raw)

Try / catch

try:
    result = tool.execute(kwargs)
except ValueError as e:
    return {"error": str(e)}  # surface message to caller/LLM for self-correction

Prevention

When it happens

Trigger: Calling execute or _portfolio_greeks with legs=None, legs="...", legs={} (a dict instead of list), or legs=[]. Often happens when the LLM/caller passes a JSON string instead of a parsed array, or omits legs entirely.

Common situations: Agent tool invocations where JSON arguments arrive as strings; clients building legs from user input that can be empty; passing an object keyed by leg index instead of an array.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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