headroomlabs-ai/headroom · error · ValueError

{key}={raw!r} is not an integer

Error message

{key}={raw!r} is not an integer

What it means

The headroom-oauth2 plugin parses `HEADROOM_OAUTH2_*` integer settings (e.g. timeout/skew env vars) with a strict `_int` helper: a value that is set, non-empty, and not parseable by `int()` raises ValueError naming the key and the raw value. This is intentional fail-closed behavior — a malformed numeric setting must abort startup rather than be silently defaulted.

Source

Thrown at plugins/headroom-oauth2/src/headroom_oauth2/__init__.py:54

            continue
        k, v = (x.strip() for x in pair.split("=", 1))
        if not k:
            continue
        if _ctrl(k) or _ctrl(v) or " " in k or ":" in k:
            log.warning("headroom-oauth2: dropping malformed static header: %r", k)
            continue
        out[k] = v
    return out


def _int(env, key):
    raw = env.get(key)
    if raw is None or not str(raw).strip():
        return None
    try:
        return int(raw)
    except ValueError:
        raise ValueError(f"{key}={raw!r} is not an integer") from None


def provider_from_env(env: dict | None = None) -> OAuth2ClientCredentials | None:
    """Build a provider from ``HEADROOM_OAUTH2_*`` env vars, or None if TOKEN_URL is unset.

    Raises ValueError on malformed config so callers can fail closed.
    """
    env = os.environ if env is None else env
    token_url = env.get("HEADROOM_OAUTH2_TOKEN_URL")
    if not token_url:
        return None
    allow_insecure = env.get("HEADROOM_OAUTH2_ALLOW_INSECURE", "").strip().lower() in (
        "1",
        "true",
        "yes",
    )
    if allow_insecure:
        log.warning("headroom-oauth2: ALLOW_INSECURE set -- token endpoint TLS check disabled")

View on GitHub (pinned to 322425c43b)

Solutions

  1. Set the variable to a plain integer string: `HEADROOM_OAUTH2_TIMEOUT_SECONDS=30` (no units, no quotes, no decimals — use `30` not `30.5`)
  2. Inspect the exact value the message shows: `echo "$HEADROOM_OAUTH2_TIMEOUT_SECONDS" | cat -A` to expose hidden quotes/whitespace/CRLF
  3. Remove the variable entirely if you want the built-in default — unset/empty is accepted and skipped, only malformed non-empty values fail

Example fix

# before
environment:
  - HEADROOM_OAUTH2_TIMEOUT_SECONDS=30s   # or "30" with literal quotes

# after
environment:
  - HEADROOM_OAUTH2_TIMEOUT_SECONDS=30
Defensive patterns

Strategy: validation

Validate before calling

def get_int_env(env: dict, key: str) -> int | None:
    raw = env.get(key)
    if raw is None or not str(raw).strip():
        return None
    try:
        return int(str(raw).strip())
    except ValueError:
        raise ValueError(f"{key} must be an integer, got {raw!r}") from None

get_int_env(os.environ, "HEADROOM_OAUTH2_TIMEOUT_SECONDS")  # before starting the proxy

Type guard

def int_env_ok(raw: str | None) -> bool:
    if raw is None or not str(raw).strip():
        return True  # unset/empty is fine
    try:
        int(str(raw).strip()); return True
    except ValueError:
        return False

Try / catch

try:
    provider = provider_from_env()
except ValueError as e:
    raise SystemExit(f"aborting: oauth2 env invalid: {e}") from e  # fail loud at deploy time

Prevention

When it happens

Trigger: Setting e.g. `HEADROOM_OAUTH2_TIMEOUT_SECONDS=30s`, `=thirty`, `=30.5` (int() rejects decimals), or a value with stray whitespace/quotes like `"60"` with literal quotes from a config file, while `HEADROOM_OAUTH2_TOKEN_URL` is also set so the provider is constructed.

Common situations: Docker-compose/Kubernetes env values quoted as strings with units; `.env` files where a value was edited and left non-numeric; YAML configs using `timeout: 30s` style durations pasted into env vars; `30.0` floats from scripts that write env files.

Related errors


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