HKUDS/Vibe-Trading · critical · TigerConfigError

Tiger private key not found at {key_path}

Error message

Tiger private key not found at {key_path}

What it means

_client_config expands cfg.private_key_path and requires the file to exist before calling tigeropen's read_private_key. A missing key file is classified as TigerConfigError rather than an auth failure, because the connector treats key material location as configuration. The path is used exactly as configured (with ~ expansion but no search of default locations).

Source

Thrown at agent/src/trading/connectors/tiger/sdk.py:565


def _require_tigeropen() -> ModuleType:
    try:
        import tigeropen  # type: ignore
    except ModuleNotFoundError as exc:
        raise TigerDependencyError("tigeropen is not installed; run `pip install tigeropen`.") from exc
    return tigeropen


def _client_config(cfg: TigerConfig):
    """Build a ``TigerOpenClientConfig`` from connector settings."""
    _require_tigeropen()
    from tigeropen.common.util.signature_utils import read_private_key  # type: ignore
    from tigeropen.tiger_open_config import TigerOpenClientConfig  # type: ignore

    key_path = Path(cfg.private_key_path).expanduser()
    if not key_path.exists():
        raise TigerConfigError(f"Tiger private key not found at {key_path}")
    client_config = TigerOpenClientConfig()
    client_config.private_key = read_private_key(str(key_path))
    client_config.tiger_id = cfg.tiger_id
    client_config.account = cfg.account
    try:
        client_config.timeout = cfg.timeout
    except Exception:  # noqa: BLE001 - older SDKs may not expose timeout
        pass
    return client_config


def _trade_client(cfg: TigerConfig):
    _require_tigeropen()
    from tigeropen.trade.trade_client import TradeClient  # type: ignore

    return TradeClient(_client_config(cfg))

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Verify the path: ls -l the exact expanded value of cfg.private_key_path
  2. Download/regenerate the RSA private key from the Tiger OpenAPI portal and place it at the configured path
  3. Use an absolute path or '~' (expanduser is applied) rather than '$HOME' or relative paths
  4. In containers, ensure the key is mounted/secret-injected before the connector starts

Example fix

# before
{"private_key_path": "/home/dev/keys/tiger.pem"}  # missing on server

# after
{"private_key_path": "/etc/vibetrading/secrets/tiger_private_key.pem"}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def key_present(cfg) -> bool:
    return bool(cfg.private_key_path) and Path(cfg.private_key_path).expanduser().is_file()

Type guard

def has_valid_key_path(cfg: TigerConfig) -> bool:
    p = Path(cfg.private_key_path or '').expanduser()
    return p.is_file() and p.stat().st_size > 0

Try / catch

try:
    positions = get_positions(cfg)
except TigerConfigError as exc:
    if 'private key not found' in str(exc):
        logger.error('Missing Tiger key at %s — mount/regenerate it', cfg.private_key_path)
    raise

Prevention

When it happens

Trigger: Calling any function that builds a Tiger client (_trade_client, _quote_client -> _client_config) when cfg.private_key_path points to a nonexistent path — typo, unexpanded env var, absolute path valid only on another machine, or key file never downloaded from Tiger's developer portal.

Common situations: Config written on a dev machine with a home-relative path that differs in deployment; SSH key generated but saved elsewhere; container lacking the mounted secret; path containing '$HOME' literally instead of '~' or an expanded absolute path.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/224119646edacbf6. Report an issue: GitHub.