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
- Log the raw assets array and find the duplicate source
- If merging responses in a wrapper, de-duplicate by asset symbol before passing data through
- 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
- De-duplicate asset rows by symbol when merging responses in wrappers
- Replace, never append, assets arrays across retries
- Log raw assets when this fires to find the duplication source
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
- Binance USD-M position reads are incoherent
- Binance USD-M Shadow Account supports USDT collateral only
- Binance USD-M margin mode is missing
- Binance USD-M isolated position requires positive isolated m
- Binance USD-M cross position must report zero isolated margi
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/17773da75a5df99a.
Report an issue: GitHub.