headroomlabs-ai/headroom · error · ValueError

workers must be >= 1

Error message

workers must be >= 1

What it means

run_server validates that the worker process count is at least 1; workers < 1 (0 or negative) is rejected with ValueError before configuration is finalized. Worker count maps onto ProxyConfig.worker_processes, which itself must be >= 1.

Source

Thrown at headroom/proxy/server.py:5263

            still gets the banner since it has no other startup output.
    """
    if not FASTAPI_AVAILABLE:
        print("ERROR: FastAPI required. Install: pip install fastapi uvicorn httpx")
        sys.exit(1)

    # Seed the request-time coding-profile toggles (tool-search, dedupe, read
    # protection, lossless→lossy, effort-router, block-char floor) into the
    # process env before serving, so downstream per-request readers pick them up.
    # Done here (not in the CLI command) so unit tests that mock run_server never
    # mutate os.environ. setdefault → explicit env still wins. MODE / profile are
    # already resolved into `config` above via their inline defaults.
    from headroom.agent_savings import seed_proxy_env_defaults

    seed_proxy_env_defaults()

    config = config or ProxyConfig()
    if workers < 1:
        raise ValueError("workers must be >= 1")
    config.worker_processes = workers
    code_aware_status = _get_code_aware_banner_status(config)

    # Format connection pool info
    pool_info = f"max={config.max_connections}, keepalive={config.max_keepalive_connections}"
    http2_status = "ENABLED" if (config.http2 and not config.http_proxy) else "DISABLED"

    backend_status = format_backend_status(
        backend=config.backend,
        anyllm_provider=config.anyllm_provider,
        bedrock_region=config.bedrock_region,
    )

    # Resolve upstream API targets for display in the banner (#583).
    api_targets = resolve_api_targets(config.provider_api_overrides)

    if print_banner:
        print(f"""

View on GitHub (pinned to 322425c43b)

Solutions

  1. Pass workers=1 for single-process serving.
  2. Clamp computed worker counts with max(1, computed).
  3. Validate CLI/env worker inputs to IntRange(min=1)-style bounds.

Example fix

# before
run_server(config=config, workers=max(0, os.cpu_count() - 8))

# after
run_server(config=config, workers=max(1, os.cpu_count() - 8))
Defensive patterns

Strategy: validation

Validate before calling

workers = max(1, int(os.environ.get("HEADROOM_WORKERS", "1")))
if workers < 1:
    raise SystemExit("workers must be >= 1")

Type guard

def valid_workers(n: int) -> bool:
    return isinstance(n, int) and n >= 1

Try / catch

try:
    run_server(config=config, workers=workers)
except ValueError as e:
    if "workers" in str(e):
        run_server(config=config, workers=1)
    else:
        raise

Prevention

When it happens

Trigger: Calling run_server(workers=0) or run_server(workers=-1); computing workers from an expression like len(tasks)-1 that can go negative; CLI flags accepting 0.

Common situations: Auto-sizing workers from CPU/load formulas that return 0; passing a CLI --workers 0 expecting single-process mode; config templating with an unset variable defaulting to 0.

Related errors


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