{"record":{"id":"3c9c7d8578e82bb6","repo":"github/spec-kit","slug":"exc","errorCode":null,"errorMessage":"{exc}","messagePattern":"\\{exc\\}","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/commands/init.py","lineNumber":115,"sourceCode":"\n    from .._assets import _locate_bundled_extension\n    from ..extensions import ExtensionCatalog, ExtensionError, ExtensionManager\n    from ..extensions._commands import (\n        _resolve_catalog_extension,\n        install_extension_from_url,\n    )\n\n    manager = ExtensionManager(project_path)\n\n    # --- URL ---\n    parsed = urlparse(ext_spec)\n    if parsed.scheme in (\"http\", \"https\"):\n        try:\n            manifest = install_extension_from_url(\n                manager, project_path, ext_spec, speckit_version\n            )\n        except ExtensionError as exc:\n            raise ValueError(str(exc)) from exc\n        return f\"{manifest.name} v{manifest.version} installed\"\n\n    # --- Local path ---\n    if ext_spec.startswith((\"./\", \"../\", \"/\", \"~/\", \".\\\\\", \"..\\\\\")) or Path(ext_spec).is_absolute():\n        source_path = Path(ext_spec).expanduser().resolve()\n        if not source_path.exists():\n            raise ValueError(f\"Directory not found: {source_path}\")\n        if not (source_path / \"extension.yml\").exists():\n            raise ValueError(f\"No extension.yml found in {source_path}\")\n        manifest = manager.install_from_directory(source_path, speckit_version)\n        return f\"{manifest.name} v{manifest.version} installed\"\n\n    # --- Bundled extension name or catalog ID ---\n    bundled_path = _locate_bundled_extension(ext_spec)\n    if bundled_path is not None:\n        if manager.registry.is_installed(ext_spec):\n            return \"already installed\"\n        manifest = manager.install_from_directory(bundled_path, speckit_version)","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/commands/init.py#L97-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the suffix of the message to identify the underlying ExtensionError category (HTTPS, download, archive format, or installation).","Verify the URL serves a real ZIP or tar.gz archive with curl and use HTTPS.","For private GitHub assets, authenticate with the configured provider or publish the extension through an approved catalog.","If the URL is untrusted in a non-interactive run, confirm trust interactively or pass --trust-extension-urls deliberately.","Fall back to a bundled/catalog extension id or a local `./path` extension directory while the URL issue is fixed."],"exampleFix":"# before\nspecify init demo --extension http://example.com/ext.zip\n\n# after\nspecify init demo --extension https://example.com/ext.zip --trust-extension-urls","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef init_extension_url_is_supported(url: str) -> bool:\n    try:\n        scheme = urlparse(url).scheme\n    except ValueError:\n        return False\n    return scheme == \"https\"","typeGuard":null,"tryCatchPattern":"try:\n    _install_extension_during_init(project_path, ext_spec, version)\nexcept ValueError as exc:\n    record_extension_step_failure(ext_spec, str(exc))\n    if not str(exc).startswith((\"URL must use HTTPS\", \"Failed to download\")):\n        raise","preventionTips":["Use direct HTTPS URLs to ZIP/tar.gz assets only.","Verify private-release URLs with the same authenticated environment before init.","Pin extension versions in project setup scripts rather than relying on mutable latest URLs."],"tags":["init","extensions","url","download","https"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}