github/spec-kit · error · PresetError
Preset '{pack_id}' has no download URL
Error message
Preset '{pack_id}' has no download URL What it means
The pack's catalog entry has no download_url at all (falsy value). After the bundled and install_allowed checks, download cannot proceed without a URL, so PresetError is raised.
Source
Thrown at src/specify_cli/presets/__init__.py:4846
if pack_info.get("bundled") and not pack_info.get("download_url"):
from ..extensions import REINSTALL_COMMAND
raise PresetError(
f"Preset '{pack_id}' is bundled with spec-kit and has no download URL. "
f"It should be installed from the local package. "
f"Use 'specify preset add {pack_id}' to install from the bundled package, "
f"or reinstall spec-kit if the bundled files are missing: {REINSTALL_COMMAND}"
)
if not pack_info.get("_install_allowed", True):
catalog_name = pack_info.get("_catalog_name", "unknown")
raise PresetError(
f"Preset '{pack_id}' is from the '{catalog_name}' catalog which does not allow installation. "
f"Use --from with the preset's repository URL instead."
)
download_url = pack_info.get("download_url")
if not download_url:
raise PresetError(
f"Preset '{pack_id}' has no download URL"
)
if not isinstance(download_url, str):
raise PresetError(
f"Preset download URL is malformed: {download_url}"
)
from urllib.parse import urlparse
# A malformed authority (e.g. an unterminated IPv6 bracket
# "https://[::1") makes urlparse / hostname access raise ValueError.
# The download_url comes from catalog payload data, so surface a clean
# PresetError rather than leaking a raw ValueError past the command
# handler (which only catches PresetError). Mirrors catalogs (#3435)
# and workflows/catalog.py (#3484).
try:
parsed = urlparse(download_url)
hostname = parsed.hostnameView on GitHub (pinned to bf88c9f9a8)
Solutions
- Fix the catalog payload: add a valid HTTPS download_url to the pack entry
- If the pack is meant to be local-only, mark it 'bundled' so callers take the local-install path
- Report the broken entry to the catalog maintainer if you do not control it
Example fix
// before (catalog entry)
{"id": "mytheme"}
// after
{"id": "mytheme", "download_url": "https://example.com/mytheme-1.0.0.tar.gz"} Defensive patterns
Strategy: validation
Validate before calling
info = manager.get_pack_info(pack_id) or {}
if not info.get("download_url") and not info.get("bundled"):
raise ValueError(f"catalog entry for {pack_id} lacks download_url") Type guard
def has_download_url(info: dict) -> bool:
return bool(info.get("download_url")) or bool(info.get("bundled")) Try / catch
except PresetError as e:
if "has no download URL" in str(e):
# upstream catalog data defect; report rather than retry
report_catalog_defect(pack_id)
raise Prevention
- Schema-validate catalog payloads (download_url present and non-empty for non-bundled packs) before publishing
- Treat a missing download_url as a data bug, not a transient failure — do not retry
When it happens
Trigger: A catalog entry that is not 'bundled' but omits download_url (or sets it to an empty string / null) — i.e. malformed third-party catalog data.
Common situations: Hand-authored catalog JSON missing the download_url field; catalog schema drift where the producer renamed the field; entry intentionally metadata-only being passed to the download path.
Related errors
- A catalog source is missing its 'id'.
- Catalog source '{source_id}' is missing its 'url'.
- Catalog source '{source_id}' is missing its 'priority'.
- Catalog payload is missing a 'bundles' object.
- Failed to fetch preset catalog from {entry.url}: {e}
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/02b098056869d535.
Report an issue: GitHub.