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

  1. Log the raw payload and verify it is actually a JSON array of objects before parsing
  2. Check the HTTP status / error body of the upstream Binance call — an error envelope commonly masquerades as this failure
  3. Fix the caller to pass response['data'] (the list) rather than the whole response dict
  4. 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

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


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