HKUDS/Vibe-Trading · error · UsdMObservationError

Binance USD-M assets must include exactly one USDT row

Error message

Binance USD-M assets must include exactly one USDT row

What it means

The futures account assets array contained more than one row whose asset is USDT. The wallet model expects exactly one USDT balance row; duplicates make balances ambiguous.

Source

Thrown at agent/src/trading/connectors/binance/usdm.py:248

def _require_usdt_assets(account: Mapping[str, Any]) -> Mapping[str, Any]:
    assets = _mapping_rows(account.get("assets"), "assets")
    usdt_asset: Mapping[str, Any] | None = None
    balance_fields = (
        "walletBalance",
        "marginBalance",
        "availableBalance",
        "initialMargin",
        "positionInitialMargin",
        "openOrderInitialMargin",
        "maintMargin",
        "unrealizedProfit",
    )
    for asset in assets:
        symbol = str(asset.get("asset") or "").upper()
        balances = tuple(_number(asset.get(field), field) for field in balance_fields)
        if symbol == "USDT":
            if usdt_asset is not None:
                raise UsdMObservationError("Binance USD-M assets must include exactly one USDT row")
            usdt_asset = asset
        elif any(value != 0 for value in balances):
            raise UsdMObservationError("Binance USD-M Shadow Account supports the USDT asset only")
    if usdt_asset is None:
        raise UsdMObservationError("Binance USD-M assets must include USDT")
    return usdt_asset


def _require_coherent_totals(
    account: Mapping[str, float],
    positions: list[dict[str, Any]],
    close_enough: CloseEnough,
) -> None:
    comparisons = (
        (
            account["total_unrealized_pnl"],
            sum(row["unrealized_pnl"] for row in positions),
        ),

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Log the raw assets array and find the duplicate source
  2. If merging responses in a wrapper, de-duplicate by asset symbol before passing data through
  3. If Binance genuinely returned duplicates, retry the request and report the anomaly

Example fix

# before (wrapper merging two responses)
assets = r1['assets'] + r2['assets']
# after
by_asset = {a['asset']: a for a in r1['assets'] + r2['assets']}
assets = list(by_asset.values())
Defensive patterns

Strategy: validation

Validate before calling

acct = await client.futures_account()
usdt_rows = [a for a in acct['assets'] if a['asset'].upper() == 'USDT']
if len(usdt_rows) != 1:
    raise RuntimeError(f"expected exactly 1 USDT row, got {len(usdt_rows)}")

Type guard

def has_single_usdt_row(assets: list[dict]) -> bool:
    return sum(1 for a in assets if str(a.get('asset', '')).upper() == 'USDT') == 1

Try / catch

try:
    obs = await connector.read_account_observation()
except UsdMObservationError as e:
    if "exactly one USDT row" in str(e):
        raise RuntimeError("response aggregation likely duplicated assets; fix wrapper")
    raise

Prevention

When it happens

Trigger: Binance returning the assets list with a repeated USDT entry (server-side anomaly), or a response assembled/merged from multiple requests (e.g. paginated or multi-account aggregation) duplicating the row.

Common situations: Custom middleware concatenating responses from v2/account and v3/account, retry logic appending instead of replacing the assets array, or mocking layers returning duplicated fixtures.

Related errors


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