{"record":{"id":"a1812aa630c97805","repo":"github/spec-kit","slug":"no-project-scoped-catalog-source-matching-target","errorCode":null,"errorMessage":"No project-scoped catalog source matching '{target}' was found.","messagePattern":"No project-scoped catalog source matching '(.+?)' was found\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/commands_impl/catalog_config.py","lineNumber":228,"sourceCode":"            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):\n        raise BundlerError(\n            f\"No project-scoped catalog source matching '{target}' was found.\"\n        )\n    _write(project_root, remaining)\n    return target\n","sourceCodeStart":210,"sourceCodeEnd":233,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/commands_impl/catalog_config.py#L210-L233","documentation":"Raised by remove_source() when neither the exact id/url match nor the canonicalized-url fallback finds a matching project-scoped source. remove_source only inspects the project catalog config (not user or built-in scopes), and the canonicalization fallback is deliberately conservative so it never deletes a different source whose url happens to equal a bare id's canonicalized path.","triggerScenarios":"Calling remove_source with a typo'd id or url; removing a source that was never added or already removed; removing a user-scoped or built-in source via the project-scoped API; passing a relative path that canonicalizes differently from how it was stored; passing a bare id whose canonicalized local path is empty-scheme and therefore not retried.","commonSituations":"Idempotent cleanup scripts that assume removal always succeeds; stale docs referencing an old source id; double-running an uninstall playbook; confusion between project scope and user scope sources.","solutions":["List the actual project sources first (catalog list / _read) and copy the exact id or stored url string to pass to remove_source.","If you added with a relative path, pass the stored absolute url (or the id) rather than the relative path.","For idempotent scripts, catch this BundlerError and treat 'not found' as success.","Confirm the source is not user-scoped or built-in — those are not removable through this project-scoped API."],"exampleFix":"# before\nremove_source(root, \"./cat.json\")  # stored as absolute url; may not match\n\n# after\nsources = read_sources(root)\nmatch = next((s for s in sources if \"cat.json\" in s.url), None)\nif match:\n    remove_source(root, match.id)","handlingStrategy":"try-catch","validationCode":"sources = catalog_config._read(root)\ntargets = {c.get(\"id\") for c in sources} | {c.get(\"url\") for c in sources}\nif target not in targets:\n    # show user the available ids/urls instead of calling remove_source","typeGuard":null,"tryCatchPattern":"try:\n    remove_source(root, target)\nexcept BundlerError as e:\n    if \"was found\" in str(e) and \"No project-scoped\" in str(e):\n        pass  # idempotent cleanup: already absent\n    else:\n        raise","preventionTips":["Pass the exact stored id or absolute url (list sources first) rather than relative paths.","Treat 'not found' as success in uninstall scripts.","Remember this API is project-scope only — user/built-in sources are invisible to it."],"tags":["bundler","catalog","not-found","remove"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}