ZhuLinsen/daily_stock_analysis · error · FutuPortfolioError
futu-api==10.8.6808 的网络层仅支持 IPv4;FUTU_OPEND_HOST 当前为 {host!r
Error message
futu-api==10.8.6808 的网络层仅支持 IPv4;FUTU_OPEND_HOST 当前为 {host!r},请改用 IPv4 地址或可解析到 IPv4 的主机名。 What it means
FutuPortfolioError raised in _connection_settings (src/brokers/futu/portfolio.py:133) when FUTU_OPEND_HOST resolves (via ipaddress.ip_address) to an IPv6 literal. The pinned futu-api==10.8.6808 network layer only supports IPv4, so the guard rejects IPv6 addresses up front (including bracketed [::1] form, which is unbracketed before parsing) with a message telling you to use an IPv4 address or an IPv4-resolvable hostname.
Source
Thrown at src/brokers/futu/portfolio.py:133
def _connection_settings() -> tuple[str, int]:
"""Return the validated IPv4 OpenD host and port from environment settings."""
host = (os.getenv("FUTU_OPEND_HOST") or "127.0.0.1").strip()
raw_port = (os.getenv("FUTU_OPEND_PORT") or "11111").strip()
try:
port = int(raw_port)
except ValueError as exc:
raise FutuPortfolioError(f"FUTU_OPEND_PORT 不是有效端口: {raw_port!r}") from exc
if not host or not 1 <= port <= 65535:
raise FutuPortfolioError(f"Futu OpenD 地址无效: {host!r}:{port}")
address_text = host[1:-1] if host.startswith("[") and host.endswith("]") else host
try:
address = ipaddress.ip_address(address_text)
except ValueError:
address = None
if address is not None and address.version != 4:
raise FutuPortfolioError(
"futu-api==10.8.6808 的网络层仅支持 IPv4;"
f"FUTU_OPEND_HOST 当前为 {host!r},请改用 IPv4 地址或可解析到 IPv4 的主机名。"
)
return host, port
def _configured_account_id() -> Optional[int]:
"""Return the optional configured real account ID."""
value = (os.getenv("FUTU_ACC_ID") or "").strip()
if not value:
return None
try:
account_id = int(value)
except ValueError as exc:
raise FutuPortfolioError("FUTU_ACC_ID 必须是正整数账户 ID") from exc
if account_id <= 0:
raise FutuPortfolioError("FUTU_ACC_ID 必须是正整数账户 ID")View on GitHub (pinned to 5159bd72e8)
Solutions
- Set FUTU_OPEND_HOST to an IPv4 literal: 127.0.0.1 for local, or the server's IPv4 address for remote.
- If using a hostname, make sure it resolves to an A record (IPv4) — check with 'dig <host> A' or 'ping <host>' — since the SDK will connect over IPv4 only.
- If OpenD is only listening on IPv6, reconfigure OpenD to bind an IPv4 address (its own config), or front it with an IPv4 proxy/port-forward (e.g. socat) and point FUTU_OPEND_HOST at that.
- Ensure any ssh tunnel/port-forward you use terminates on an IPv4 bind address.
Example fix
# before (.env) FUTU_OPEND_HOST=::1 # after (.env) FUTU_OPEND_HOST=127.0.0.1
Defensive patterns
Strategy: validation
Validate before calling
import ipaddress
def opend_host_is_ipv4(host: str) -> bool:
text = host[1:-1] if host.startswith('[') and host.endswith(']') else host
try:
return ipaddress.ip_address(text).version == 4
except ValueError:
return True # hostname: resolution checked at connect time Prevention
- Prefer IPv4 literals (127.0.0.1, LAN IPv4) for FUTU_OPEND_HOST with futu-api 10.8.6808.
- If using hostnames, verify an A record exists (dig <host> A).
- Configure port-forwards/tunnels with IPv4 bind addresses.
When it happens
Trigger: Setting FUTU_OPEND_HOST=::1 or [::1] to reach a local OpenD; entering an IPv6 LAN address like fe80::1 when OpenD listens on IPv6; systems where localhost preferentially resolves to ::1 — note hostnames are NOT rejected here, only literals, so 'localhost' passes but must resolve to IPv4 at connect time.
Common situations: Modern hosts where 127.0.0.1 is replaced with ::1 out of habit; IPv6-only Docker networks; remote OpenD reached over a tunnel that yields IPv6 addresses.
Related errors
- FUTU_OPEND_PORT 不是有效端口: {raw_port!r}
- Futu OpenD 地址无效: {host!r}:{port}
- FUTU_ACC_ID 必须是正整数账户 ID
- 不支持的 FUTU_SECURITY_FIRM: {name}
- 查询 Futu 真实账户失败: {data}
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/d6674a9e27be8433.
Report an issue: GitHub.