github/spec-kit · error · PresetValidationError
Catalog URL is malformed: {url}
Error message
Catalog URL is malformed: {url} What it means
Raised while validating a catalog URL: urllib.parse.urlparse raised ValueError while parsing, meaning the URL string is malformed enough that even parsing fails (e.g. invalid characters in the scheme/host, unmatched brackets). Reported as PresetValidationError with the offending URL.
Source
Thrown at src/specify_cli/presets/__init__.py:4151
self.cache_file = self.cache_dir / "catalog.json"
self.cache_metadata_file = self.cache_dir / "catalog-metadata.json"
def _validate_catalog_url(self, url: str) -> None:
"""Validate that a catalog URL uses HTTPS (localhost HTTP allowed).
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.View on GitHub (pinned to bf88c9f9a8)
Solutions
- Fix the URL string in preset-catalogs.yml (or the CLI argument) — ensure scheme://host[:port]/path form.
- For IPv6, use proper bracketed literals: `https://[::1]:8443/`.
- Sanitize programmatic inputs (strip whitespace/quotes) before treating them as URLs.
Example fix
# before catalogs: - url: "https://[::1:8443/catalog.json" # after catalogs: - url: "https://[::1]:8443/catalog.json"
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
try:
parsed = urlparse(url)
except ValueError:
raise ValueError(f"malformed catalog URL: {url!r}") Try / catch
try:
manager.add_catalog(url) # or the validating call
except PresetValidationError as e:
if "malformed" in str(e):
# re-enter the URL; check for truncated IPv6 brackets or stray characters
... Prevention
- Sanitize URLs from config/CLI input (strip whitespace and quotes).
- Bracket IPv6 literals correctly: https://[::1]:8443/.
- Pre-validate with urlparse before writing catalog config.
When it happens
Trigger: Passing a catalog URL containing an invalid IPv6 literal (`http://[::1`), a URL with a bad scheme delimiter, or non-URL garbage that trips urlparse's parser rather than merely producing empty components.
Common situations: Typos in preset-catalogs.yml or CLI --catalog arguments, copy-paste artifacts (trailing brackets, control characters), or templating that mangles the URL string.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Catalog URL must be a valid URL with a host.
- Invalid catalog url: '{url}'.
- Unsupported catalog url scheme '{parsed.scheme}://' in '{url
- Catalog url must be a valid URL with a host: {url}
- Catalog entry '{resolved.entry.id}' has a malformed download
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/6f6492f7732d28c5.
Report an issue: GitHub.