{"record":{"id":"c8ad0c15e0ab6d75","repo":"ZhuLinsen/daily_stock_analysis","slug":"futu-id","errorCode":null,"errorMessage":"Futu 账户查询返回了无效账户 ID","messagePattern":"Futu 账户查询返回了无效账户 ID","errorType":"exception","errorClass":"FutuPortfolioError","httpStatus":null,"severity":"error","filePath":"src/brokers/futu/portfolio.py","lineNumber":197,"sourceCode":"        )\n        ret, data = context.get_acc_list()\n        if ret != api.RET_OK:\n            raise FutuPortfolioError(f\"查询 Futu 真实账户失败: {data}\")\n        for row in _iter_rows(data, \"Futu 账户查询\"):\n            if _enum_text(row.get(\"trd_env\")) != \"REAL\":\n                continue\n            if _enum_text(row.get(\"acc_status\")) != \"ACTIVE\":\n                continue\n            if _enum_text(row.get(\"acc_role\")) not in _SUPPORTED_ACCOUNT_ROLES:\n                continue\n            raw_acc_id = row.get(\"acc_id\")\n            try:\n                acc_id = int(raw_acc_id)\n                exact_integer = isinstance(raw_acc_id, str) or bool(\n                    raw_acc_id == acc_id\n                )\n            except (TypeError, ValueError, OverflowError) as exc:\n                raise FutuPortfolioError(\n                    \"Futu 账户查询返回了无效账户 ID\"\n                ) from exc\n            if isinstance(raw_acc_id, bool) or not exact_integer or acc_id <= 0:\n                raise FutuPortfolioError(\"Futu 账户查询返回了无效账户 ID\")\n            if acc_id in seen_ids:\n                continue\n            returned_firm_name = _enum_text(row.get(\"security_firm\"))\n            returned_firm = getattr(\n                api.SecurityFirm,\n                returned_firm_name,\n                security_firm,\n            )\n            seen_ids.add(acc_id)\n            accounts.append(_FutuAccount(acc_id=acc_id, security_firm=returned_firm))\n    except FutuPortfolioError:\n        raise\n    except Exception as exc:  # noqa: BLE001 - translate SDK/network failures\n        raise FutuPortfolioError(f\"查询 Futu 真实账户失败: {exc}\") from exc","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/brokers/futu/portfolio.py#L179-L215","documentation":"FutuPortfolioError raised in _discover_real_accounts (src/brokers/futu/portfolio.py:197) when the acc_id cell of a get_acc_list row cannot be converted to int — int() raises TypeError (None/missing cell), ValueError (non-numeric string like 'N/A'), or OverflowError (float infinity). This indicates the OpenD/SDK response is malformed, since account IDs are always numeric.","triggerScenarios":"A DataFrame row where acc_id is None or NaN (masked by filtering), a placeholder string, or an SDK serialization glitch after an OpenD version mismatch with the pinned SDK.","commonSituations":"Running a newer/older OpenD whose payload schema differs from futu-api==10.8.6808 expectations; corrupted OpenD session state; mocks in tests returning rows without acc_id.","solutions":["Align versions: use futu-api==10.8.6808 with a compatible OpenD build (check Futu's version matrix).","Restart OpenD to clear degraded session state, then retry the account query.","Log the raw row (row.to_dict()) when this fires to see exactly what OpenD returned; report to Futu if the payload is genuinely malformed.","In tests, always include a valid integer acc_id in stubbed rows."],"exampleFix":"# before (test stub missing acc_id)\npd.DataFrame([{'trd_env': 'REAL', 'acc_status': 'ACTIVE'}])\n\n# after\npd.DataFrame([{'acc_id': 1234567, 'trd_env': 'REAL', 'acc_status': 'ACTIVE', 'acc_role': 'NORMAL'}])","handlingStrategy":"validation","validationCode":"def row_has_int_acc_id(row) -> bool:\n    raw = row.get('acc_id')\n    if raw is None or isinstance(raw, bool):\n        return False\n    try:\n        return int(raw) > 0 and (isinstance(raw, str) or int(raw) == raw)\n    except (TypeError, ValueError, OverflowError):\n        return False","typeGuard":"def is_valid_acc_id_cell(raw: object) -> bool:\n    if isinstance(raw, bool) or raw is None:\n        return False\n    try:\n        v = int(raw)\n    except (TypeError, ValueError, OverflowError):\n        return False\n    return v > 0 and (isinstance(raw, str) or v == raw)","tryCatchPattern":null,"preventionTips":["Keep OpenD and futu-api versions in the tested pairing (10.8.6808).","Always include a valid integer acc_id in SDK test fixtures.","Log raw rows on discovery failure to catch payload schema drift early."],"tags":["futu","sdk-contract","data-integrity"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}