headroomlabs-ai/headroom · error · RuntimeError

{_WRAP_PROXY_TIMEOUT_ENV} must be a positive integer number

Error message

{_WRAP_PROXY_TIMEOUT_ENV} must be a positive integer number of seconds (got {raw!r})

What it means

The wrap proxy readiness timeout is configurable via the HEADROOM_WRAP_PROXY_TIMEOUT env var (referenced by _WRAP_PROXY_TIMEOUT_ENV). _resolve_wrap_proxy_timeout_seconds parses it strictly: it must be a base-10 integer greater than zero. Non-numeric text ('30s', '1.5', '') or values <= 0 raise RuntimeError naming the variable and echoing the offending raw value; empty/unset falls back to a default and never raises.

Source

Thrown at headroom/cli/wrap.py:537

def _default_wrap_proxy_timeout_seconds() -> int:
    """Return the default wrap proxy startup timeout for this environment."""

    if _ml_wrap_extras_detected():
        return _WRAP_PROXY_TIMEOUT_ML_DEFAULT_SECONDS
    return _WRAP_PROXY_TIMEOUT_DEFAULT_SECONDS


def _resolve_wrap_proxy_timeout_seconds() -> int:
    """Resolve the wrap proxy readiness timeout from env or defaults."""

    raw = os.environ.get(_WRAP_PROXY_TIMEOUT_ENV, "").strip()
    if not raw:
        return _default_wrap_proxy_timeout_seconds()

    try:
        timeout_seconds = int(raw)
    except ValueError:
        raise RuntimeError(
            f"{_WRAP_PROXY_TIMEOUT_ENV} must be a positive integer number of seconds (got {raw!r})"
        ) from None
    if timeout_seconds <= 0:
        raise RuntimeError(
            f"{_WRAP_PROXY_TIMEOUT_ENV} must be a positive integer number of seconds (got {raw!r})"
        )
    return timeout_seconds


def _print_telemetry_notice() -> None:
    """Print a telemetry notice when anonymous telemetry is enabled.

    Respects the HEADROOM_TELEMETRY and HEADROOM_TELEMETRY_WARN feature flags.
    Does nothing when telemetry or warnings are disabled.
    """
    from headroom.telemetry.beacon import format_telemetry_notice

    notice = format_telemetry_notice(prefix="  ")

View on GitHub (pinned to 322425c43b)

Solutions

  1. Set a plain positive integer: export HEADROOM_WRAP_PROXY_TIMEOUT=120
  2. Remove unit suffixes and decimals — '90' not '90s', '2' not '2.0'
  3. To get the default behavior, unset the variable entirely rather than setting 0
  4. If you intended a longer startup window for ML extras, raise the integer (defaults are larger when ML extras are detected)

Example fix

# before
export HEADROOM_WRAP_PROXY_TIMEOUT=90s
headroom wrap -- claude
# RuntimeError: ... must be a positive integer number of seconds (got '90s')

# after
export HEADROOM_WRAP_PROXY_TIMEOUT=90
headroom wrap -- claude
Defensive patterns

Strategy: validation

Validate before calling

import os

raw = os.environ.get("HEADROOM_WRAP_PROXY_TIMEOUT", "").strip()
if raw:
    try:
        v = int(raw)
        assert v > 0, "must be > 0"
    except (ValueError, AssertionError):
        raise SystemExit(
            "HEADROOM_WRAP_PROXY_TIMEOUT must be a positive integer (e.g. 90, not '90s')"
        )

Type guard

def valid_timeout(raw: str | None) -> bool:
    if raw is None or not raw.strip():
        return True  # unset falls back to defaults
    try:
        return int(raw.strip()) > 0
    except ValueError:
        return False

Try / catch

try:
    run_wrap_command()
except RuntimeError as e:
    if "HEADROOM_WRAP_PROXY_TIMEOUT" in str(e):
        os.environ.pop("HEADROOM_WRAP_PROXY_TIMEOUT", None)  # fall back to default
        run_wrap_command()
    else:
        raise

Prevention

When it happens

Trigger: Exporting HEADROOM_WRAP_PROXY_TIMEOUT with a unit suffix ('90s'), a float ('1.5'), a negative number, or stray whitespace/characters ('60 '), then running `headroom wrap ...` which calls this resolver.

Common situations: Copy-pasting timeout values with units from docs or docker-compose examples ('30s' is valid YAML duration syntax but not here); CI env matrices setting 0 to 'disable' the timeout; shell quoting artifacts.

Understand the failure class

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/8e85901878648fc5. Report an issue: GitHub.