HKUDS/Vibe-Trading · error · UsdMObservationError
Binance USD-M {name} payload is invalid
Error message
Binance USD-M {name} payload is invalid What it means
Raised by _mapping_rows when a payload expected to be a list of Mapping rows (e.g. positionRisk or balance arrays) is not a list, or contains elements that are not Mapping-like. It is a shape check that runs before any field extraction on exchange responses. Any non-list (dict, None, string) or non-dict element triggers it.
Source
Thrown at agent/src/trading/connectors/binance/usdm.py:286
account["total_initial_margin"],
sum(row["initial_margin"] for row in positions),
),
(
account["total_maintenance_margin"],
sum(row["maintenance_margin"] for row in positions),
),
(
account["margin_balance"],
account["wallet_balance"] + account["total_unrealized_pnl"],
),
)
if any(not close_enough(reported, derived) for reported, derived in comparisons):
raise UsdMObservationError("Binance USD-M account totals are incoherent")
def _mapping_rows(value: Any, name: str) -> list[Mapping[str, Any]]:
if not isinstance(value, list) or any(not isinstance(row, Mapping) for row in value):
raise UsdMObservationError(f"Binance USD-M {name} payload is invalid")
return value
def _require_one_way(rows: list[Mapping[str, Any]]) -> None:
if any(str(row.get("positionSide") or "").upper() != "BOTH" for row in rows):
raise UsdMObservationError("Binance USD-M Shadow Account requires one-way position mode")
def _active_by_symbol(rows: list[Mapping[str, Any]], source: str) -> dict[str, Mapping[str, Any]]:
active: dict[str, Mapping[str, Any]] = {}
for row in rows:
quantity = _number(row.get("positionAmt"), "positionAmt")
if quantity == 0:
continue
symbol = str(row.get("symbol") or "").upper()
_canonical_symbol(symbol)
if symbol in active:
raise UsdMObservationError(f"duplicate {source} position symbol")View on GitHub (pinned to 80ffdda44c)
Solutions
- Log the raw payload and verify it is actually a JSON array of objects before parsing
- Check the HTTP status / error body of the upstream Binance call — an error envelope commonly masquerades as this failure
- Fix the caller to pass response['data'] (the list) rather than the whole response dict
- Update mocks/fixtures to return lists of objects
Example fix
// before
rows = response # dict {"code":0, "msg":"ok", "data":[...]}
_join_positions(rows)
// after
rows = response["data"]
_join_positions(rows) Defensive patterns
Strategy: type-guard
Validate before calling
from collections.abc import Mapping
def is_row_list(value) -> bool:
return isinstance(value, list) and all(isinstance(r, Mapping) for r in value)
rows = response.get("data") if isinstance(response, dict) else response
assert is_row_list(rows), f"unexpected payload shape: {type(response)}" Type guard
from collections.abc import Mapping
from typing import Any
def is_payload_rows(value: Any) -> TypeGuard[list[Mapping[str, Any]]]:
return isinstance(value, list) and all(isinstance(r, Mapping) for r in value) Try / catch
try:
obs = connector.read_account_observation(...)
except UsdMObservationError as exc:
if "payload is invalid" in str(exc):
logger.error("bad payload shape from exchange: %s", raw_response)
raise Prevention
- Check HTTP status and error codes before parsing exchange bodies
- Validate list-of-objects shape before passing payloads
- Extract nested arrays (response['data']) explicitly
When it happens
Trigger: Passing a dict keyed by symbol instead of a list of rows to _join_positions or _require_usdt_assets; a None payload from a failed upstream call; elements that are strings or numbers instead of objects; API responses where Binance wrapped the array in an error object.
Common situations: Binance returning an error object {'code': -1, 'msg': ...} instead of the expected array; mocks returning json.loads of the wrong fixture; upstream HTTP client returning None on non-200 without raising; version changes in the connector changing the expected payload shape.
Related errors
- Binance USD-M assets must include USDT
- Binance USD-M account totals are incoherent
- Binance USD-M Shadow Account requires one-way position mode
- duplicate {source} position symbol
- Binance USD-M field {name} must be numeric
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/4f57a4b8caa883d1.
Report an issue: GitHub.