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

  1. Correct the download_url in the catalog entry (close brackets, fix syntax) and re-cache.
  2. Validate catalog URLs at publish time: python -c "from urllib.parse import urlparse; urlparse(url)".
  3. 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

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

Related errors


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