github/spec-kit · error · BundlerError
Catalog entry '{resolved.entry.id}' has a malformed download
Error message
Catalog entry '{resolved.entry.id}' has a malformed download_url: {url} What it means
urlparse raised ValueError on the catalog entry's download_url — a malformed URL that Python cannot even parse, e.g. an unclosed IPv6 literal bracket ('https://[::1'). The CLI converts it to BundlerError (with `from None` to hide the noisy chain) so the contract 'callers only catch BundlerError' holds, instead of leaking a raw ValueError past the command handlers.
Source
Thrown at src/specify_cli/commands/bundle/__init__.py:864
``.zip`` artifact also works), which :func:`_local_manifest_source` handles
before catalog resolution and which never touches ``download_url``.
"""
from urllib.parse import urlparse
url = resolved.entry.download_url
if not url:
raise BundlerError(
f"Catalog entry '{resolved.entry.id}' has no download_url; cannot resolve "
"its manifest."
)
# A malformed authority (e.g. an unclosed IPv6 bracket ``https://[::1``)
# makes urlparse raise ValueError. Surface it as the documented
# BundlerError, like the sibling ``_validate_remote_url``, rather than
# leaking a raw ValueError past the callers, which only catch BundlerError.
try:
parsed = urlparse(url)
except ValueError:
raise BundlerError(
f"Catalog entry '{resolved.entry.id}' has a malformed download_url: {url}"
) from None
scheme = parsed.scheme.lower()
# ``file://`` URLs and bare filesystem paths (including Windows drive paths
# like ``C:\bundle.yml``, which urlparse reads as a single-letter scheme)
# are not valid catalog download URLs. Catalog URLs are HTTPS-only across
# every catalog system; installing from disk is done by passing the path
# positionally, which never reaches URL resolution. Give an actionable
# error rather than accepting a scheme the rest of the codebase rejects.
if scheme in ("", "file") or re.match(r"^[A-Za-z]:[\\/]", url):
raise BundlerError(
f"Catalog entry '{resolved.entry.id}' has a non-HTTP(S) download_url "
f"({url}); catalog download URLs must be HTTPS (http for localhost) — "
"a file:// URL, a local filesystem path, or a scheme-less value "
"(e.g. 'example.com/bundle.zip') is not accepted. "
"To install a bundle from disk, pass the path directly: "
"'specify bundle install <path-to-bundle.yml | bundle-dir | .zip>'."View on GitHub (pinned to bf88c9f9a8)
Solutions
- Correct the download_url in the catalog entry (close brackets, fix syntax) and re-cache.
- Validate catalog URLs at publish time: python -c "from urllib.parse import urlparse; urlparse(url)".
- Work around locally by installing from a path artifact.
Example fix
# before (catalog entry) "download_url": "https://[::1/bundle.zip" # after "download_url": "https://[::1]/bundle.zip"
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
url = resolved.entry.download_url
try:
urlparse(url)
except ValueError:
raise SystemExit(f"Malformed catalog download_url: {url!r}") Type guard
def is_parseable_url(url: str) -> bool:
from urllib.parse import urlparse
try:
urlparse(url)
return True
except ValueError:
return False Try / catch
try:
manifest = _download_manifest(resolved, offline=offline)
except BundlerError as exc:
if "malformed download_url" in str(exc):
# fix the catalog entry URL; meanwhile install from a local path
... Prevention
- Run urlparse() over catalog URLs when generating/publishing catalogs.
- Watch for truncated IPv6 literals and templating artifacts in generated catalogs.
When it happens
Trigger: A catalog entry with download_url like 'https://[::1' (unclosed bracket) or another urlparse-invalid authority; any install/update that must fetch that entry's manifest.
Common situations: Hand-edited catalog files with typos; templating pipelines truncating URLs; IPv6-local test registries written incorrectly.
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
- Invalid catalog url: '{url}'.
- Unsupported catalog url scheme '{parsed.scheme}://' in '{url
- Catalog url must be a valid URL with a host: {url}
- Malformed catalog config at {path}: expected a mapping at th
- Malformed catalog config at {path}: 'catalogs' must be a lis
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/b0c46648de7255d2.
Report an issue: GitHub.