Skip to content

ConnectionManager.reconnect logs attempt failures at DEBUG with no exc_info — root cause lost #355

Closed

Description

@TexasCoding

Problem

ConnectionManager.reconnect logs every failed attempt at DEBUG with no exc_info. When ws_max_retries (default 10) is exhausted the user sees only a generic KalshiConnectionError: Max reconnect attempts (10) exceeded with no __cause__ — the last exception was discarded by continue. Common failure modes (expired RSA key, revoked key ID, bad ws_base_url, TLS handshake error, DNS failure) are indistinguishable to the operator.

The REST transport's retry path surfaces the underlying exception by chaining via from. This WS path drops it. In production an operator looking at logs sees ten escalating-backoff warnings and a generic final error with no signal whether to rotate the key, escalate to Kalshi support, or check their NAT. The DEBUG attempt log is typically off in prod.

Evidence

  • kalshi/ws/connection.py:186-204:
logger.warning(
    "Reconnecting in %.1fs (attempt %d/%d)",
    delay,
    attempt + 1,
    self._config.ws_max_retries,
)
await asyncio.sleep(delay)
try:
    await self._set_state(ConnectionState.CONNECTING)
    self._ws = await self._open_socket()
    await self._set_state(ConnectionState.CONNECTED)
    return
except Exception:
    logger.debug("Reconnect attempt %d failed", attempt + 1)
    continue
await self._set_state(ConnectionState.CLOSED)
raise KalshiConnectionError(
    f"Max reconnect attempts ({self._config.ws_max_retries}) exceeded"
)

No exc_info=True, no last_exc captured, no raise ... from last_exc.

Suggested fix

Capture the latest exception and (a) log at warning with exc_info=True per attempt so an operator with the default log level sees what's going wrong, and (b) chain the final raise via from last_exc so the underlying failure stays on the traceback:

last_exc: BaseException | None = None
for attempt in range(self._config.ws_max_retries):
    ...
    try:
        ...
    except Exception as exc:
        last_exc = exc
        logger.warning(
            "Reconnect attempt %d/%d failed",
            attempt + 1, self._config.ws_max_retries,
            exc_info=True,
        )
        continue
await self._set_state(ConnectionState.CLOSED)
raise KalshiConnectionError(
    f"Max reconnect attempts ({self._config.ws_max_retries}) exceeded"
) from last_exc

Matches the REST transport's existing pattern.

Source

Round-3 independent audit (reviewer: websocket).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    polishCode-quality and DX improvements; non-functionalwsWebSocket-related

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions