github/spec-kit · error · ExtensionError
{url} did not return a ZIP archive or tar.gz/tgz archive (go
Error message
{url} did not return a ZIP archive or tar.gz/tgz archive (got {len(archive_data)} bytes). This usually means the request was not authenticated and a login/HTML page was returned. Verify the URL and configured credentials. What it means
detect_archive_format could not identify the downloaded bytes as ZIP or tar.gz/tgz, so the installer refuses to proceed and explains the most common cause: the URL returned an HTML login/error page instead of an archive. The message includes the byte count as a hint (HTML pages are typically a few KB).
Source
Thrown at src/specify_cli/extensions/_commands.py:199
raise ExtensionError(
f"Could not safely write download file: {exc}"
) from exc
format_source = (
final_url
if archive_format_from_name(final_url) is not None
else url
)
try:
detect_archive_format(
archive_path,
archive_file=download_file,
source_name=format_source,
content_type=content_type,
error_type=ExtensionError,
)
except ExtensionError as exc:
raise ExtensionError(
f"{url} did not return a ZIP archive or tar.gz/tgz archive "
f"(got {len(archive_data)} bytes). This usually means the request "
"was not authenticated and a login/HTML page was returned. "
"Verify the URL and configured credentials."
) from exc
# Consume the transient inode reserved above rather than reopening the
# cache pathname during extraction.
try:
return manager.install_from_zip(
archive_path,
speckit_version,
priority=priority,
force=force,
archive_file=download_file,
)
except OSError as exc:
raise ExtensionError(View on GitHub (pinned to bf88c9f9a8)
Solutions
- Inspect what actually came back: `curl -sL <url> | head -c 300` — HTML confirms the auth/redirect theory
- Use the direct raw/download asset URL (e.g. .../releases/download/<tag>/<file> or raw.githubusercontent.com) rather than a UI page
- Provide valid credentials to the fetch environment or make the artifact public
- Download manually with auth, then `specify extension install ./my-ext.zip`
Example fix
# before: blob page URL, returns HTML specify extension install https://github.com/org/repo/blob/main/ext.zip # after: actual asset specify extension install https://github.com/org/repo/releases/download/v1.0.0/ext.zip
Defensive patterns
Strategy: validation
Validate before calling
import urllib.request
def url_serves_archive(url: str) -> bool:
with urllib.request.urlopen(url, timeout=10) as r:
ctype = (r.headers.get('Content-Type') or '').lower()
head = r.read(4)
return (
head[:2] == b'PK' # ZIP magic
or head[:2] == b'\x1f\x8b' # gzip magic
or 'octet-stream' in ctype or 'zip' in ctype or 'tar' in ctype
) and 'text/html' not in ctype Try / catch
from specify_cli.extensions import ExtensionError
try:
install_from_url_cmd(project_root, url, speckit_version)
except ExtensionError as e:
if 'did not return a ZIP archive' in str(e):
# response was HTML: fix auth or use the raw asset URL, then retry Prevention
- Verify the URL serves bytes (PK/gzip magic), not a page, before scripted installs
- Use direct asset/raw URLs; keep credentials valid for private artifacts
When it happens
Trigger: `specify extension install <url>` where the server returns 200 with HTML/text: unauthenticated private GitHub/GitLab repo redirecting to a login page, a pre-signed URL error in XML/JSON, a soft-404 page, or pointing at a regular file that isn't an archive (e.g. a .py or plain README).
Common situations: Private repo release assets without credentials; expired tokens rendered as an HTML error; copy-pasting a blob page URL instead of the raw asset; web servers serving directory listings.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- {exc}
- Failed to download from {url}: {exc}
- Could not safely create download file: {exc}
- Could not safely write download file: {exc}
- ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRE
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/27e27bd924895749.
Report an issue: GitHub.