github/spec-kit · error · BundlerError

Catalog source '{resolved_id}' (or url) already exists in th

Error message

Catalog source '{resolved_id}' (or url) already exists in this project.

What it means

Raised by add_source() when registering a catalog source whose resolved id or canonical url collides with an entry already present in the project's catalog config. The function canonicalizes the url (e.g. expands relative local paths to absolute file paths) and derives an id from it when none is supplied, so the duplicate check covers both explicit and derived collisions. This protects the project catalog list from ambiguous, shadowing entries.

Source

Thrown at src/specify_cli/bundler/commands_impl/catalog_config.py:191

        # or "https://user@". Validating here keeps junk out of
        # bundle-catalogs.yml instead of failing later at fetch time.
        is_localhost = hostname in ("localhost", "127.0.0.1", "::1")
        if parsed.scheme.lower() != "https" and not is_localhost:
            raise BundlerError(
                f"Catalog url must use HTTPS (got {parsed.scheme}://). "
                "HTTP is only allowed for localhost."
            )
        if not hostname:
            raise BundlerError(f"Catalog url must be a valid URL with a host: {url}")

    url = _canonicalize_url(url)
    install_policy = InstallPolicy.parse(policy)
    resolved_id = (source_id or _derive_id(url)).strip()

    catalogs = _read(project_root)
    for existing in catalogs:
        if existing.get("id") == resolved_id or existing.get("url") == url:
            raise BundlerError(
                f"Catalog source '{resolved_id}' (or url) already exists in this project."
            )

    entry = {
        "id": resolved_id,
        "url": url,
        "priority": int(priority),
        "install_policy": install_policy.value,
    }
    catalogs.append(entry)
    _write(project_root, catalogs)
    return CatalogSource.from_dict(entry, Scope.PROJECT)


def remove_source(project_root: Path, id_or_url: str) -> str:
    target = id_or_url.strip()
    if target in _BUILTIN_IDS:
        raise BundlerError(

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Remove the existing source first: remove_source(project_root, '<existing-id-or-url>'), then re-add it with the new settings.
  2. Check the current project catalog (via the list/read API or the catalog config file) before calling add_source, and skip when the id or canonical url is already present.
  3. If the collision is only on url but you want a distinct id, pass an explicit source_id and a different url (or vice versa) only if a genuinely different source is intended.
  4. Make idempotent provisioning scripts tolerant: catch this BundlerError and treat 'already exists' as success.

Example fix

# before
add_source(root, "community", "https://example.com/catalog.json")  # second run raises

# after
existing = {c.id: c for c in read_sources(root)}
if "community" not in existing:
    add_source(root, "community", "https://example.com/catalog.json")
Defensive patterns

Strategy: validation

Validate before calling

from specify_cli.bundler.commands_impl import catalog_config

def add_source_safe(root, source_id, url, priority, policy):
    existing = catalog_config._read(root)
    for c in existing:
        if c.get("id") == source_id or c.get("url") == url:
            return None  # already present
    return catalog_config.add_source(root, source_id, url, priority, policy)

Try / catch

try:
    add_source(root, sid, url, priority=10, policy="install-allowed")
except BundlerError as e:
    if "already exists" in str(e):
        pass  # idempotent provisioning: treat as success
    else:
        raise

Prevention

When it happens

Trigger: Calling add_source(project_root, url=...) twice with the same url; calling add_source with a source_id equal to an existing entry's id; adding a local path that _canonicalize_url() expands to the same absolute url as a previously added source; adding a built-in id that already appears in project scope.

Common situations: Re-running a setup script or 'specify bundler catalog add' command twice; adding a source that was already added in an earlier session; copy-pasting onboarding docs that add the same default catalog; CI provisioning steps that run unconditionally instead of checking current state.

Related errors


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