github/spec-kit · error · TemplateResolutionError

Invalid extension registry {registry}: not a regular file

Error message

Invalid extension registry {registry}: not a regular file

What it means

The streaming branch of the downloader: the response either had no (or unparseable/lying) Content-Length, and while reading in _DOWNLOAD_CHUNK_SIZE (64 KiB) chunks the running total exceeded max_bytes (5 MiB). This enforces the cap even when the header is absent or understates the body.

Source

Thrown at scripts/python/common.py:248

        except Exception:
            pass
    try:
        return sorted(
            p.name
            for p in presets_dir.iterdir()
            if p.is_dir() and _is_safe_component(p.name)
        )
    except OSError:
        return []


def _sorted_extension_ids(extensions_dir: Path) -> list[str]:
    registry = extensions_dir / ".registry"
    registered_ids: set[str] = set()
    extensions: dict[object, object] = {}
    if os.path.lexists(registry):
        if not registry.is_file():
            raise TemplateResolutionError(
                f"Invalid extension registry {registry}: not a regular file"
            )
        try:
            data = json.loads(registry.read_text(encoding="utf-8"))
        except (OSError, UnicodeError, json.JSONDecodeError) as exc:
            raise TemplateResolutionError(
                f"Failed to parse extension registry {registry}: {exc}"
            ) from exc
        if not isinstance(data, dict):
            raise TemplateResolutionError(
                f"Invalid extension registry {registry}: root must be a mapping"
            )
        raw_extensions = data.get("extensions", {})
        if not isinstance(raw_extensions, dict):
            raise TemplateResolutionError(
                f"Invalid extension registry {registry}: "
                "'extensions' must be a mapping"
            )

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Fetch the URL manually (curl -sL <url> | wc -c) to see the true payload size and fix the source URL to point at the workflow YAML itself
  2. Ensure the URL serves the raw workflow file with accurate metadata, not a dynamically generated page
  3. If the workflow genuinely needs more than 5 MiB, reduce it (externalize assets) or install it locally from a file path instead of HTTP
  4. Pre-validate with a manual size check before invoking the CLI in automated pipelines

Example fix

# before
specify workflow add https://cdn.example.com/pack?page=all  # chunked, unbounded

# after
specify workflow add https://raw.example.com/workflows/my-workflow.yaml
# or install from a local file that you have size-checked
Defensive patterns

Strategy: validation

Validate before calling

import urllib.request

MAX = 5 * 1024 * 1024
req = urllib.request.Request(url, method='GET', headers={'Range': f'bytes=0-{MAX}'})
with urllib.request.urlopen(req) as r:
    data = r.read(MAX + 1)
if len(data) > MAX:
    raise ValueError(f'body exceeds {MAX} bytes even before full download')

Try / catch

try:
    data = download_workflow_source(url)
except ValueError as e:
    if 'workflow size limit' in str(e):
        # check true size, then fix or replace the source URL
        raise SystemExit(f'actual payload too large: {e}') from e
    raise

Prevention

When it happens

Trigger: `workflow add <url>` where the server omits Content-Length (chunked transfer) or lies about it, and the actual body exceeds 5 MiB; streaming endpoints that keep sending data; gzip-encoded responses whose decoded size balloons past the limit.

Common situations: Chunked transfer from CDNs/serverless endpoints without Content-Length; misconfigured proxies stripping or falsifying headers; URLs that redirect to a login/download page of unbounded size; archives or generated bundles served without metadata.

Related errors


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