{"record":{"id":"868b5f1c9d45d39b","repo":"ZhuLinsen/daily_stock_analysis","slug":"futu-opend-port-raw-port-r","errorCode":null,"errorMessage":"FUTU_OPEND_PORT 不是有效端口: {raw_port!r}","messagePattern":"FUTU_OPEND_PORT 不是有效端口: (.+?)","errorType":"exception","errorClass":"FutuPortfolioError","httpStatus":null,"severity":"error","filePath":"src/brokers/futu/portfolio.py","lineNumber":123,"sourceCode":"    \"\"\"Close an SDK context without masking the primary operation result.\"\"\"\n\n    if context is None:\n        return\n    try:\n        context.close()\n    except Exception:  # pragma: no cover - closing is best effort\n        logger.debug(\"关闭 Futu OpenD 连接失败\", exc_info=True)\n\n\ndef _connection_settings() -> tuple[str, int]:\n    \"\"\"Return the validated IPv4 OpenD host and port from environment settings.\"\"\"\n\n    host = (os.getenv(\"FUTU_OPEND_HOST\") or \"127.0.0.1\").strip()\n    raw_port = (os.getenv(\"FUTU_OPEND_PORT\") or \"11111\").strip()\n    try:\n        port = int(raw_port)\n    except ValueError as exc:\n        raise FutuPortfolioError(f\"FUTU_OPEND_PORT 不是有效端口: {raw_port!r}\") from exc\n    if not host or not 1 <= port <= 65535:\n        raise FutuPortfolioError(f\"Futu OpenD 地址无效: {host!r}:{port}\")\n\n    address_text = host[1:-1] if host.startswith(\"[\") and host.endswith(\"]\") else host\n    try:\n        address = ipaddress.ip_address(address_text)\n    except ValueError:\n        address = None\n    if address is not None and address.version != 4:\n        raise FutuPortfolioError(\n            \"futu-api==10.8.6808 的网络层仅支持 IPv4；\"\n            f\"FUTU_OPEND_HOST 当前为 {host!r}，请改用 IPv4 地址或可解析到 IPv4 的主机名。\"\n        )\n    return host, port\n\n\ndef _configured_account_id() -> Optional[int]:\n    \"\"\"Return the optional configured real account ID.\"\"\"","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/brokers/futu/portfolio.py#L105-L141","documentation":"FutuPortfolioError raised in _connection_settings (src/brokers/futu/portfolio.py:123) when the FUTU_OPEND_PORT environment variable is set but int() cannot parse it (e.g. 'abc', '11 111', ''). Empty/unset falls back to the default 11111; only a set-but-non-numeric value raises.","triggerScenarios":"Setting FUTU_OPEND_PORT to a non-integer string in .env, docker-compose, or GitHub Actions secrets; copy-paste artifacts like quotes, spaces, a port range '11111-11112', or a URL fragment; a stray newline or BOM in a .env file.","commonSituations":"Typo in .env ('FUTU_OPEND_PORT=111l1'); accidentally pasting the full host:port into the port var; Windows line endings or invisible characters in env files loaded via dotenv.","solutions":["Set FUTU_OPEND_PORT to a plain integer between 1 and 65535 (default OpenD port is 11111), or unset it to use the default.","Check for whitespace/quotes/CRLF in the .env value; the code strips spaces but not other artifacts — retype the line cleanly.","If set in docker/Actions, print the resolved value at startup (e.g. repr) to catch injection artifacts."],"exampleFix":"# before (.env)\nFUTU_OPEND_PORT=\"11111 \"   # or 11O11 with letter O\n\n# after (.env)\nFUTU_OPEND_PORT=11111","handlingStrategy":"validation","validationCode":"def valid_opend_port(raw: str | None) -> bool:\n    if raw is None or not raw.strip():\n        return True  # default 11111 used\n    try:\n        return 1 <= int(raw.strip()) <= 65535\n    except ValueError:\n        return False","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep FUTU_OPEND_PORT as a bare integer (usually 11111) in .env and CI variables.","Unset the variable rather than setting placeholder values.","Validate env-derived ints at process start with a fail-fast config check."],"tags":["configuration","environment","futu","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}