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
- Make every element a dict: {"option_type": ..., "strike": ..., "qty": ...}
- If data arrives as tuples, map them: legs=[{"option_type":t,"strike":s,"qty":q} for t,s,q in raw]
- 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
- Use a pydantic model per leg and parse with type enforcement
- Never encode legs as tuples or strings
- Validate array element types before tool dispatch
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
- legs must be a non-empty array
- T must be > 0 to imply a volatility, got {T}
- audit must be a list
- audit rows must be objects
- legs may contain at most {_MAX_LEGS} entries
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/4860bba086cd2579.
Report an issue: GitHub.