ZhuLinsen/daily_stock_analysis · error · FutuPortfolioError

FUTU_OPEND_PORT 不是有效端口: {raw_port!r}

Error message

FUTU_OPEND_PORT 不是有效端口: {raw_port!r}

What it means

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.

Source

Thrown at src/brokers/futu/portfolio.py:123

    """Close an SDK context without masking the primary operation result."""

    if context is None:
        return
    try:
        context.close()
    except Exception:  # pragma: no cover - closing is best effort
        logger.debug("关闭 Futu OpenD 连接失败", exc_info=True)


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."""

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Set FUTU_OPEND_PORT to a plain integer between 1 and 65535 (default OpenD port is 11111), or unset it to use the default.
  2. Check for whitespace/quotes/CRLF in the .env value; the code strips spaces but not other artifacts — retype the line cleanly.
  3. If set in docker/Actions, print the resolved value at startup (e.g. repr) to catch injection artifacts.

Example fix

# before (.env)
FUTU_OPEND_PORT="11111 "   # or 11O11 with letter O

# after (.env)
FUTU_OPEND_PORT=11111
Defensive patterns

Strategy: validation

Validate before calling

def valid_opend_port(raw: str | None) -> bool:
    if raw is None or not raw.strip():
        return True  # default 11111 used
    try:
        return 1 <= int(raw.strip()) <= 65535
    except ValueError:
        return False

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/868b5f1c9d45d39b. Report an issue: GitHub.