HKUDS/Vibe-Trading · error · ValueError

legs[{index}] must be an object

Error message

legs[{index}] must be an object

What it means

Thrown while iterating legs when an element is not a JSON object (dict). Each leg must be a mapping with option_type/strike/qty keys; a scalar, string, list, or null element triggers this with the offending index in the message.

Source

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

                "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")
        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

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Make every element a dict: {"option_type": ..., "strike": ..., "qty": ...}
  2. If data arrives as tuples, map them: legs=[{"option_type":t,"strike":s,"qty":q} for t,s,q in raw]
  3. Validate the whole array shape client-side before invoking the tool

Example fix

// before
execute({"legs": ["call", 100, 1], ...})
// after
execute({"legs": [{"option_type": "call", "strike": 100, "qty": 1}], ...})
Defensive patterns

Strategy: type-guard

Validate before calling

bad = [i for i, x in enumerate(legs) if not isinstance(x, dict)]
if bad:
    raise ValueError(f"legs entries not objects at indices {bad}")

Type guard

def legs_all_objects(legs) -> bool:
    return isinstance(legs, list) and all(isinstance(x, dict) for x in legs)

Try / catch

try:
    execute(kwargs)
except ValueError as e:
    if "must be an object" in str(e):
        fix_element(int(str(e).split('[')[1].split(']')[0]))

Prevention

When it happens

Trigger: legs=[100, 200], legs=["call@100"], legs=[["call",100,1]], or legs=[null]. Typical when callers encode legs as compact tuples/strings instead of objects.

Common situations: LLM tool calls that compress legs into shorthand formats; CSV/TSV import code mapping rows to scalars; mixed malformed data from user-edited JSON.

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/4860bba086cd2579. Report an issue: GitHub.