{"record":{"id":"88e03154f2cab544","repo":"HKUDS/Vibe-Trading","slug":"duplicate-source-position-symbol","errorCode":null,"errorMessage":"duplicate {source} position symbol","messagePattern":"duplicate (.+?) position symbol","errorType":"exception","errorClass":"UsdMObservationError","httpStatus":null,"severity":"error","filePath":"agent/src/trading/connectors/binance/usdm.py","lineNumber":304,"sourceCode":"        raise UsdMObservationError(f\"Binance USD-M {name} payload is invalid\")\n    return value\n\n\ndef _require_one_way(rows: list[Mapping[str, Any]]) -> None:\n    if any(str(row.get(\"positionSide\") or \"\").upper() != \"BOTH\" for row in rows):\n        raise UsdMObservationError(\"Binance USD-M Shadow Account requires one-way position mode\")\n\n\ndef _active_by_symbol(rows: list[Mapping[str, Any]], source: str) -> dict[str, Mapping[str, Any]]:\n    active: dict[str, Mapping[str, Any]] = {}\n    for row in rows:\n        quantity = _number(row.get(\"positionAmt\"), \"positionAmt\")\n        if quantity == 0:\n            continue\n        symbol = str(row.get(\"symbol\") or \"\").upper()\n        _canonical_symbol(symbol)\n        if symbol in active:\n            raise UsdMObservationError(f\"duplicate {source} position symbol\")\n        active[symbol] = row\n    return active\n\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:","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/trading/connectors/binance/usdm.py#L286-L322","documentation":"Raised by _active_by_symbol when two non-zero position rows share the same uppercased symbol in the same source payload. In one-way mode each symbol must appear at most once among active positions, so a duplicate indicates a malformed or contradictory positionRisk (or, historically, hedge-mode rows for the same symbol). The connector refuses to guess which row is authoritative.","triggerScenarios":"_join_positions processes a positionRisk list containing two rows with symbol 'BTCUSDT' and non-zero positionAmt; commonly happens when hedge-mode LONG and SHORT rows are both present after collapsing, or when a mock concatenates two snapshots.","commonSituations":"Account switched modes leaving stale duplicate rows; test data duplicating symbols; retry logic appending instead of replacing the previous payload; exchange returning merged cross-margin and isolated rows for the same symbol in a shape the connector doesn't dedupe.","solutions":["Inspect the raw positionRisk payload for duplicate symbols with non-zero positionAmt","If LONG/SHORT rows for the same symbol appear, resolve the hedge-mode positions (see error 1223) — the account is not in one-way mode","Deduplicate or regenerate mock data so each symbol appears once","Re-fetch a fresh snapshot; transient partial responses can self-correct on retry"],"exampleFix":"// before\n[{\"symbol\":\"BTCUSDT\",\"positionAmt\":\"1\"}, {\"symbol\":\"BTCUSDT\",\"positionAmt\":\"-1\"}]\n// after\n[{\"symbol\":\"BTCUSDT\",\"positionAmt\":\"0\"}]","handlingStrategy":"validation","validationCode":"from collections import Counter\n\ndef no_duplicate_active_symbols(rows: list[dict]) -> bool:\n    active = [str(r.get(\"symbol\", \"\")).upper() for r in rows if float(r.get(\"positionAmt\", 0)) != 0]\n    return len(active) == len(set(active))\n\nassert no_duplicate_active_symbols(rows)","typeGuard":null,"tryCatchPattern":"try:\n    obs = connector.read_account_observation(...)\nexcept UsdMObservationError as exc:\n    if \"duplicate\" in str(exc):\n        rows = refetch_positions()  # fresh snapshot, retry once\n    else:\n        raise","preventionTips":["Deduplicate position rows by symbol before processing","Replace, never append, cached position payloads on refetch","Keep the account in one-way mode"],"tags":["binance","usdm","duplicate-symbols","positions","validation"],"backgroundTag":"duplicate-data-validation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}