{"record":{"id":"8c8609de9f9a1370","repo":"HKUDS/Vibe-Trading","slug":"binance-usd-m-field-name-must-be-numeric","errorCode":null,"errorMessage":"Binance USD-M field {name} must be numeric","messagePattern":"Binance USD-M field (.+?) must be numeric","errorType":"exception","errorClass":"UsdMObservationError","httpStatus":null,"severity":"error","filePath":"agent/src/trading/connectors/binance/usdm.py","lineNumber":325,"sourceCode":"\n\ndef _canonical_symbol(symbol: str) -> str:\n    if not symbol.endswith(\"USDT\") or len(symbol) <= 4 or not symbol.isalnum():\n        raise UsdMObservationError(\"Binance USD-M supports canonical */USDT symbols only\")\n    return f\"{symbol[:-4]}-USDT-PERP\"\n\n\ndef _number(\n    value: Any,\n    name: str,\n    *,\n    non_negative: bool = False,\n    positive: bool = False,\n) -> float:\n    try:\n        number = float(value)\n    except (TypeError, ValueError) as exc:\n        raise UsdMObservationError(f\"Binance USD-M field {name} must be numeric\") from exc\n    if not math.isfinite(number):\n        raise UsdMObservationError(f\"Binance USD-M field {name} must be finite\")\n    if positive and number <= 0:\n        raise UsdMObservationError(f\"Binance USD-M field {name} must be positive\")\n    if non_negative and number < 0:\n        raise UsdMObservationError(f\"Binance USD-M field {name} must be non-negative\")\n    return number\n\n\ndef _integer(value: Any, name: str) -> int:\n    number = _number(value, name, non_negative=True)\n    if not number.is_integer():\n        raise UsdMObservationError(f\"Binance USD-M field {name} must be an integer\")\n    return int(number)\n\n\ndef _configuration_hash(source_profile: str, host: str, absolute_tolerance: float) -> str:\n    payload = {","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/trading/connectors/binance/usdm.py#L307-L343","documentation":"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).","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Log the offending row and field named in the message; confirm the field is present and a number in the raw response","If fields were renamed in a newer Binance API version, map them before parsing","Fix fixtures to include all required numeric fields","Treat None-values that mean zero as \"0\" explicitly before calling if the business logic allows"],"exampleFix":"// before\nrow = {\"symbol\": \"BTCUSDT\"}  # positionAmt missing -> None\n_number(row.get(\"positionAmt\"), \"positionAmt\")\n// after\nrow = {\"symbol\": \"BTCUSDT\", \"positionAmt\": \"0\"}\n_number(row.get(\"positionAmt\"), \"positionAmt\")","handlingStrategy":"type-guard","validationCode":"def numeric_or_fail(row: dict, key: str):\n    v = row.get(key)\n    if v is None or isinstance(v, bool):\n        return None  # caller decides default\n    try:\n        return float(v)\n    except (TypeError, ValueError):\n        return None\n\nrow[\"positionAmt\"] = numeric_or_fail(row, \"positionAmt\") or 0.0","typeGuard":"def is_numeric_field(value: Any) -> TypeGuard[float | int | str]:\n    if isinstance(value, bool) or value is None:\n        return False\n    try:\n        float(value)\n        return True\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    obs = connector.read_account_observation(...)\nexcept UsdMObservationError as exc:\n    if \"must be numeric\" in str(exc):\n        logger.error(\"malformed field in exchange payload: %s\", exc)\n        drop_and_refetch()\n    raise","preventionTips":["Schema-validate exchange payloads (pydantic/jsonschema) before use","Default missing numeric keys explicitly to 0 where semantically zero","Pin the Binance API version the connector targets"],"tags":["binance","usdm","type-validation","numeric-fields","parsing"],"backgroundTag":"field-type-validation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}