github/spec-kit · error · PresetValidationError

Catalog URL must use HTTPS (got {parsed.scheme}://). HTTP is

Error message

Catalog URL must use HTTPS (got {parsed.scheme}://). HTTP is only allowed for localhost.

What it means

Catalog URLs must use HTTPS, with a single exception: plain HTTP is allowed for localhost (hostname exactly localhost, 127.0.0.1, or ::1). Any other scheme — http against a remote host, ftp, file — fails this PresetValidationError. This exists because catalog fetches send auth headers and must not leak over plaintext connections.

Source

Thrown at src/specify_cli/presets/__init__.py:4156

        Args:
            url: URL to validate

        Raises:
            PresetValidationError: If URL is invalid or uses non-HTTPS scheme
        """
        from urllib.parse import urlparse

        try:
            parsed = urlparse(url)
            hostname = parsed.hostname
        except ValueError:
            raise PresetValidationError(f"Catalog URL is malformed: {url}") from None
        is_localhost = hostname in ("localhost", "127.0.0.1", "::1")
        if parsed.scheme != "https" and not (
            parsed.scheme == "http" and is_localhost
        ):
            raise PresetValidationError(
                f"Catalog URL must use HTTPS (got {parsed.scheme}://). "
                "HTTP is only allowed for localhost."
            )
        # Check hostname, not netloc: netloc is truthy for host-less URLs like
        # "https://:8080" or "https://user@", so the host guarantee this error
        # promises would not actually hold. hostname is None in those cases (#3209).
        if not hostname:
            raise PresetValidationError(
                "Catalog URL must be a valid URL with a host."
            )

    def _make_request(self, url: str):
        """Build a urllib Request, adding auth headers when a provider matches.

        Delegates to :func:`specify_cli.authentication.http.build_request`.
        """
        from specify_cli.authentication.http import build_request
        return build_request(url)

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Serve the catalog over HTTPS (put it behind TLS or a proxy) and use the https:// URL.
  2. For local development, keep the host literal: http://localhost:8000, http://127.0.0.1:8000, or http://[::1]:8000.
  3. If you use a hostname alias for localhost, switch to the literal 127.0.0.1 to stay within the exception.

Example fix

# before
catalogs:
  - url: "http://catalog.internal/catalog.json"

# after
catalogs:
  - url: "https://catalog.internal/catalog.json"
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
p = urlparse(url)
host = p.hostname
assert p.scheme == "https" or (p.scheme == "http" and host in ("localhost", "127.0.0.1", "::1")), \
    "catalog URL must be https (http only for localhost)"

Type guard

from urllib.parse import urlparse
def is_allowed_catalog_url(url: str) -> bool:
    try:
        p = urlparse(url)
    except ValueError:
        return False
    return p.scheme == "https" or (p.scheme == "http" and p.hostname in ("localhost", "127.0.0.1", "::1"))

Try / catch

try:
    manager.add_catalog(url)
except PresetValidationError as e:
    if "must use HTTPS" in str(e):
        # switch the entry to https, or use a literal localhost host for dev
        ...

Prevention

When it happens

Trigger: Configuring a catalog entry with `http://internal.example.com/catalog.json` (remote HTTP), `ftp://...`, or an http:// URL whose host is a machine name or 0.0.0.0 (not in the localhost allowlist). Note: only the literal hostname counts — `http://127.0.0.1.nip.io` is rejected.

Common situations: Pointing at an internal/staging catalog served over plain HTTP inside a VPN, or a local dev server bound to a LAN hostname instead of localhost.

Related errors


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