github/spec-kit · error · ValueError

{exc}

Error message

{exc}

What it means

During `specify init --extension <url>`, URL-based extension installs are delegated to the hardened extension downloader. Any ExtensionError from that downloader — HTTPS policy, network/HTTP failure, a non-archive response, or archive installation failure — is converted to ValueError so the init step tracker can display it. The text after the colon is the original extension error.

Source

Thrown at src/specify_cli/commands/init.py:115

    from .._assets import _locate_bundled_extension
    from ..extensions import ExtensionCatalog, ExtensionError, ExtensionManager
    from ..extensions._commands import (
        _resolve_catalog_extension,
        install_extension_from_url,
    )

    manager = ExtensionManager(project_path)

    # --- URL ---
    parsed = urlparse(ext_spec)
    if parsed.scheme in ("http", "https"):
        try:
            manifest = install_extension_from_url(
                manager, project_path, ext_spec, speckit_version
            )
        except ExtensionError as exc:
            raise ValueError(str(exc)) from exc
        return f"{manifest.name} v{manifest.version} installed"

    # --- Local path ---
    if ext_spec.startswith(("./", "../", "/", "~/", ".\\", "..\\")) or Path(ext_spec).is_absolute():
        source_path = Path(ext_spec).expanduser().resolve()
        if not source_path.exists():
            raise ValueError(f"Directory not found: {source_path}")
        if not (source_path / "extension.yml").exists():
            raise ValueError(f"No extension.yml found in {source_path}")
        manifest = manager.install_from_directory(source_path, speckit_version)
        return f"{manifest.name} v{manifest.version} installed"

    # --- Bundled extension name or catalog ID ---
    bundled_path = _locate_bundled_extension(ext_spec)
    if bundled_path is not None:
        if manager.registry.is_installed(ext_spec):
            return "already installed"
        manifest = manager.install_from_directory(bundled_path, speckit_version)

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Read the suffix of the message to identify the underlying ExtensionError category (HTTPS, download, archive format, or installation).
  2. Verify the URL serves a real ZIP or tar.gz archive with curl and use HTTPS.
  3. For private GitHub assets, authenticate with the configured provider or publish the extension through an approved catalog.
  4. If the URL is untrusted in a non-interactive run, confirm trust interactively or pass --trust-extension-urls deliberately.
  5. Fall back to a bundled/catalog extension id or a local `./path` extension directory while the URL issue is fixed.

Example fix

# before
specify init demo --extension http://example.com/ext.zip

# after
specify init demo --extension https://example.com/ext.zip --trust-extension-urls
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def init_extension_url_is_supported(url: str) -> bool:
    try:
        scheme = urlparse(url).scheme
    except ValueError:
        return False
    return scheme == "https"

Try / catch

try:
    _install_extension_during_init(project_path, ext_spec, version)
except ValueError as exc:
    record_extension_step_failure(ext_spec, str(exc))
    if not str(exc).startswith(("URL must use HTTPS", "Failed to download")):
        raise

Prevention

When it happens

Trigger: Passing an http(s) URL to `specify init --extension`, where install_extension_from_url rejects the URL or cannot download/install it. The extension step fails but init can continue with other steps unless the caller aborts.

Common situations: An unauthenticated private GitHub release URL returns an HTML login page, the URL is HTTP for a non-localhost host, the archive is not ZIP/tar.gz, a proxy blocks the request, or the archive is corrupted.

Related errors


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