github/spec-kit · error · ValueError

providers[{i}]: each host must be a non-empty string

Error message

providers[{i}]: each host must be a non-empty string

What it means

Every element of a provider's hosts array must be a non-empty string after stripping (h.strip() must be truthy). Non-string elements (numbers, null, objects) or whitespace-only/empty strings raise ValueError('providers[i]: each host must be a non-empty string'). Hosts are then normalized to lowercase and validated against the wildcard pattern policy.

Source

Thrown at src/specify_cli/authentication/config.py:133

    raw = json.loads(config_path.read_text(encoding="utf-8"))

    if not isinstance(raw, dict):
        raise ValueError(f"auth.json must be a JSON object, got {type(raw).__name__}")

    providers_raw = raw.get("providers")
    if not isinstance(providers_raw, list):
        raise ValueError("auth.json must contain a 'providers' array")

    entries: list[AuthConfigEntry] = []
    for i, entry_raw in enumerate(providers_raw):
        if not isinstance(entry_raw, dict):
            raise ValueError(f"providers[{i}]: must be a JSON object")

        hosts = entry_raw.get("hosts")
        if not isinstance(hosts, list) or not hosts:
            raise ValueError(f"providers[{i}]: 'hosts' must be a non-empty array")
        if not all(isinstance(h, str) and h.strip() for h in hosts):
            raise ValueError(f"providers[{i}]: each host must be a non-empty string")
        # Normalize hosts: strip whitespace and lowercase
        hosts = [h.strip().lower() for h in hosts]
        # Reject dangerous wildcard forms (e.g. *github.com matches github.com.evil.com)
        for h in hosts:
            if not _is_valid_host_pattern(h):
                raise ValueError(
                    f"providers[{i}]: invalid host pattern {h!r}. "
                    "Only exact hostnames or '*.suffix' forms are allowed "
                    "(e.g. 'github.com' or '*.visualstudio.com')."
                )

        provider = entry_raw.get("provider", "")
        if not isinstance(provider, str) or not provider:
            raise ValueError(f"providers[{i}]: 'provider' must be a non-empty string")

        auth = entry_raw.get("auth", "")
        if not isinstance(auth, str) or not auth:
            raise ValueError(f"providers[{i}]: 'auth' must be a non-empty string")

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Remove empty/whitespace entries from the hosts array, keeping only real hostnames.
  2. In templated configs, default the variable or skip emitting the provider when the host is unset.
  3. Validate with jq: jq '.providers | map(.hosts | map(select((type!="string") or (.|trim==""))))' auth.json should yield only empty arrays.

Example fix

// before
{ "hosts": ["", "github.com"] }
// after
{ "hosts": ["github.com"] }
Defensive patterns

Strategy: validation

Validate before calling

raw = json.loads(Path("auth.json").read_text(encoding="utf-8"))
bad = [
    (i, h) for i, e in enumerate(raw.get("providers", []))
    for h in e.get("hosts", [])
    if not isinstance(h, str) or not h.strip()
]
if bad:
    raise SystemExit(f"hosts must be non-empty strings; bad values: {bad}")

Type guard

from typing import Any

def hosts_are_clean_strings(entry: Any) -> bool:
    """True when every host in the entry is a non-blank string."""
    hosts = entry.get("hosts", []) if isinstance(entry, dict) else []
    return all(isinstance(h, str) and h.strip() for h in hosts)

Try / catch

try:
    entries = load_auth_config(path)
except ValueError as exc:
    if "each host must be a non-empty string" in str(exc):
        raise SystemExit(f"remove blank/non-string hosts: {exc}") from exc
    raise

Prevention

When it happens

Trigger: "hosts": [""] or [" "]; hosts: [null] from a template variable that expanded to nothing; hosts: [8080] or a port number accidentally placed where the hostname belongs; trailing-comma artifacts producing empty strings.

Common situations: Templated auth.json where ${HOST} is unset, yielding an empty string; configs edited to blank out a host instead of removing it; mixing 'hostname:port' values that a downstream transform split into non-strings.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/0106a3cac34c7266. Report an issue: GitHub.