{"record":{"id":"6c06b8f2416dc9dc","repo":"github/spec-kit","slug":"target-is-a-built-in-default-source-and-cannot","errorCode":null,"errorMessage":"'{target}' is a built-in default source and cannot be deleted (add a same-id source to override it instead).","messagePattern":"'(.+?)' is a built-in default source and cannot be deleted \\(add a same-id source to override it instead\\)\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/commands_impl/catalog_config.py","lineNumber":209,"sourceCode":"            raise BundlerError(\n                f\"Catalog source '{resolved_id}' (or url) already exists in this project.\"\n            )\n\n    entry = {\n        \"id\": resolved_id,\n        \"url\": url,\n        \"priority\": int(priority),\n        \"install_policy\": install_policy.value,\n    }\n    catalogs.append(entry)\n    _write(project_root, catalogs)\n    return CatalogSource.from_dict(entry, Scope.PROJECT)\n\n\ndef remove_source(project_root: Path, id_or_url: str) -> str:\n    target = id_or_url.strip()\n    if target in _BUILTIN_IDS:\n        raise BundlerError(\n            f\"'{target}' is a built-in default source and cannot be deleted \"\n            \"(add a same-id source to override it instead).\"\n        )\n\n    catalogs = _read(project_root)\n    # Prefer an exact id/url match.\n    remaining = [c for c in catalogs if c.get(\"id\") != target and c.get(\"url\") != target]\n    if len(remaining) == len(catalogs):\n        # No exact match. add_source canonicalizes a local path to an absolute\n        # url before storing, so fall back to a canonicalized-url match -- this\n        # lets `remove ./cat.json` undo `add ./cat.json` (stored absolute).\n        # Only as a *fallback*: _canonicalize_url treats a bare id as a local\n        # path (empty scheme), so applying it unconditionally could also delete a\n        # different source whose url equals the id's canonicalized path.\n        canonical = _canonicalize_url(target)\n        if canonical != target:\n            remaining = [c for c in catalogs if c.get(\"url\") != canonical]\n    if len(remaining) == len(catalogs):","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/commands_impl/catalog_config.py#L191-L227","documentation":"Raised by remove_source() when the id or url passed to remove matches one of the built-in default catalog source ids (_BUILTIN_IDS). Built-in sources form the always-present fallback of the source stack and cannot be deleted; the supported way to neutralize or replace one is to add a project-scoped source with the same id, which overrides the built-in at higher precedence.","triggerScenarios":"Calling remove_source(project_root, '<builtin-id>') where <builtin-id> is listed in _BUILTIN_IDS; attempting to clean-slate the catalog by removing every source including defaults; scripting 'remove all' without filtering built-ins.","commonSituations":"Users trying to disable a default catalog they do not want; cleanup scripts that iterate the full stack and remove each source; misunderstanding that built-ins are code-defined, not entries in the project config file.","solutions":["To override a built-in, call add_source() with the same source_id and your desired url/priority/policy — same-id higher-precedence sources win.","To suppress a built-in's install capability, re-add it with install_policy='discovery-only'.","If you genuinely need it gone from listings, filter it out in your own display layer; the stack always includes built-ins.","In 'remove all' scripts, skip ids found in _BUILTIN_IDS before calling remove_source."],"exampleFix":"# before\nremove_source(root, \"official\")  # 'official' is a built-in -> raises\n\n# after\nadd_source(root, \"official\", \"https://mirror.example.com/catalog.json\",\n           priority=10, policy=\"discovery-only\")  # overrides the built-in","handlingStrategy":"validation","validationCode":"BUILTIN_IDS = set(catalog_config._BUILTIN_IDS)\n\ndef remove_source_safe(root, target):\n    if target.strip() in BUILTIN_IDS:\n        return None  # not removable; override via add_source with same id\n    return catalog_config.remove_source(root, target)","typeGuard":null,"tryCatchPattern":"try:\n    remove_source(root, target)\nexcept BundlerError as e:\n    if \"built-in default source\" in str(e):\n        add_source(root, target, override_url, priority=1, policy=\"discovery-only\")\n    else:\n        raise","preventionTips":["Never assume the catalog list is fully user-owned; filter built-ins before bulk remove.","To neutralize a built-in, add a same-id project source with the desired policy instead of deleting.","Read the built-in ids from _BUILTIN_IDS rather than hard-coding them."],"tags":["bundler","catalog","builtin","remove","configuration"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}