HKUDS/Vibe-Trading · error · UsdMObservationError

Binance USD-M field {name} must be numeric

Error message

Binance USD-M field {name} must be numeric

What it means

Raised by _number when a numeric field from an exchange payload cannot be converted with float(value) — i.e. it is None, a non-numeric string, or another non-numeric type. Every quantity/price/balance field extracted by the connector goes through this guard, so it fires on the first malformed field, naming it via {name} (e.g. positionAmt, walletBalance).

Source

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


def _canonical_symbol(symbol: str) -> str:
    if not symbol.endswith("USDT") or len(symbol) <= 4 or not symbol.isalnum():
        raise UsdMObservationError("Binance USD-M supports canonical */USDT symbols only")
    return f"{symbol[:-4]}-USDT-PERP"


def _number(
    value: Any,
    name: str,
    *,
    non_negative: bool = False,
    positive: bool = False,
) -> float:
    try:
        number = float(value)
    except (TypeError, ValueError) as exc:
        raise UsdMObservationError(f"Binance USD-M field {name} must be numeric") from exc
    if not math.isfinite(number):
        raise UsdMObservationError(f"Binance USD-M field {name} must be finite")
    if positive and number <= 0:
        raise UsdMObservationError(f"Binance USD-M field {name} must be positive")
    if non_negative and number < 0:
        raise UsdMObservationError(f"Binance USD-M field {name} must be non-negative")
    return number


def _integer(value: Any, name: str) -> int:
    number = _number(value, name, non_negative=True)
    if not number.is_integer():
        raise UsdMObservationError(f"Binance USD-M field {name} must be an integer")
    return int(number)


def _configuration_hash(source_profile: str, host: str, absolute_tolerance: float) -> str:
    payload = {

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Log the offending row and field named in the message; confirm the field is present and a number in the raw response
  2. If fields were renamed in a newer Binance API version, map them before parsing
  3. Fix fixtures to include all required numeric fields
  4. Treat None-values that mean zero as "0" explicitly before calling if the business logic allows

Example fix

// before
row = {"symbol": "BTCUSDT"}  # positionAmt missing -> None
_number(row.get("positionAmt"), "positionAmt")
// after
row = {"symbol": "BTCUSDT", "positionAmt": "0"}
_number(row.get("positionAmt"), "positionAmt")
Defensive patterns

Strategy: type-guard

Validate before calling

def numeric_or_fail(row: dict, key: str):
    v = row.get(key)
    if v is None or isinstance(v, bool):
        return None  # caller decides default
    try:
        return float(v)
    except (TypeError, ValueError):
        return None

row["positionAmt"] = numeric_or_fail(row, "positionAmt") or 0.0

Type guard

def is_numeric_field(value: Any) -> TypeGuard[float | int | str]:
    if isinstance(value, bool) or value is None:
        return False
    try:
        float(value)
        return True
    except (TypeError, ValueError):
        return False

Try / catch

try:
    obs = connector.read_account_observation(...)
except UsdMObservationError as exc:
    if "must be numeric" in str(exc):
        logger.error("malformed field in exchange payload: %s", exc)
        drop_and_refetch()
    raise

Prevention

When it happens

Trigger: A row passed to _account_values/_join_positions/_require_usdt_assets/_active_by_symbol/_integer has a numeric field that is None or non-numeric text; e.g. positionAmt missing (row.get returns None) or balance = 'N/A'.

Common situations: Binance omitting optional fields that are absent for isolated-margin accounts or new symbol types; mocks with missing keys; API version change renaming fields (e.g. marginBalance → totalMarginBalance); error envelopes where fields are absent.

Related errors


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