usestrix/strix · error · RelayError

unavailable

unavailable

Error message

unavailable

What it means

RelayError('unavailable') raised by _post_json() in strix/interface/viewer/auth.py when the HTTP request to the Strix relay (app.strix.ai) raises requests.RequestException — connection refused, DNS failure, TLS error, or timeout. It is the generic network-level failure for all relay endpoints (/api/oss/otp/start, /otp/verify, /feedback, /report/send).

Source

Thrown at strix/interface/viewer/auth.py:154

def _post_json(path: str, payload: dict[str, Any], *, timeout: int) -> tuple[int, dict[str, Any]]:
    """POST JSON to the relay. Returns (status, parsed body).

    Raises RelayError("unavailable") for network/transport failures. HTTP
    error responses (4xx/5xx) are returned as (status, body) for the caller to
    map, not raised.
    """
    url = f"{_app_url()}{path}"
    try:
        with requests.post(
            url,
            json=payload,
            headers={"Accept": "application/json"},
            timeout=timeout,
        ) as response:
            return response.status_code, _parse_body(response.content)
    except requests.RequestException as exc:
        logger.warning("relay request to %s failed: %s", path, exc)
        raise RelayError("unavailable") from exc


def _parse_body(raw: bytes) -> dict[str, Any]:
    try:
        data = json.loads(raw or b"{}")
    except json.JSONDecodeError:
        return {}
    return data if isinstance(data, dict) else {}


def otp_start(email: str) -> None:
    """Ask the relay to email a verification code. Raises RelayError on failure."""
    status, data = _post_json("/api/oss/otp/start", {"email": email}, timeout=_OTP_TIMEOUT)
    if status == 200:
        return
    if status == 429:
        raise RelayError("rate_limited")
    if status == 400:

View on GitHub (pinned to 8551339130)

Solutions

  1. Check basic connectivity: curl -sS https://app.strix.ai/ -o /dev/null -w '%{http_code}'
  2. Configure proxy env vars if egress requires them: export HTTPS_PROXY=http://proxy.corp:8080
  3. Retry after a short delay — transient network/DNS failures commonly clear
  4. If behind TLS interception, ensure the corporate CA is in the trust store (REQUESTS_CA_BUNDLE=/path/to/ca.pem)
Defensive patterns

Strategy: retry

Validate before calling

import socket, urllib.request

def relay_reachable(url="https://app.strix.ai") -> bool:
    try:
        urllib.request.urlopen(url, timeout=5)
        return True
    except Exception:
        return False

Try / catch

from strix.interface.viewer.auth import RelayError

for attempt in range(3):
    try:
        otp_start(email)
        break
    except RelayError as e:
        if e.code == "unavailable" and attempt < 2:
            time.sleep(2 ** attempt)
            continue
        raise

Prevention

When it happens

Trigger: Any otp_start(), otp_verify(), feedback_submit(), or report_send() call while offline, behind a blocking proxy, with DNS resolution failure for the app URL, or when the relay endpoint returns an unmapped status code (the final 'raise RelayError("unavailable")' fallbacks). Also raised by otp_verify when a 200 response lacks a parseable expires_at (gate fails closed).

Common situations: Corporate egress proxy or firewall blocking app.strix.ai; airplane mode / flaky WiFi during OTP verification; relay temporarily down or returning 5xx; self-signed TLS interception appliance; typo in a custom app URL override.

Related errors


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/db835d202cc7746d. Report an issue: GitHub.