From 2ac56353804583fe83d2f9ac26e6c708f796b22b Mon Sep 17 00:00:00 2001 From: marcelsafin <179933638+marcelsafin@users.noreply.github.com> Date: Fri, 31 Jul 2026 10:25:45 +0200 Subject: [PATCH] fix(bundler): wrap local catalog decode failures Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/specify_cli/bundler/services/adapters.py | 6 +++--- tests/unit/test_bundler_adapters.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/specify_cli/bundler/services/adapters.py b/src/specify_cli/bundler/services/adapters.py index f6a1d466ba..ca39a2489b 100644 --- a/src/specify_cli/bundler/services/adapters.py +++ b/src/specify_cli/bundler/services/adapters.py @@ -18,7 +18,7 @@ from ..._assets import _locate_core_pack, _repo_root from ..._download_security import MAX_JSON_CATALOG_BYTES, read_response_limited from .. import BundlerError -from ..lib.yamlio import loads_json +from ..lib.yamlio import load_json, loads_json from ..models.catalog import CatalogSource from ..models.manifest import ComponentRef @@ -145,13 +145,13 @@ def fetch(source: CatalogSource) -> dict: path = _file_url_to_path(parsed) if not path.exists(): raise BundlerError(f"Catalog file not found: {path}") - return loads_json(path.read_text(encoding="utf-8"), origin=str(path)) + return load_json(path) if scheme == "" or _is_windows_drive_path(url): path = Path(url) if not path.exists(): raise BundlerError(f"Catalog file not found: {path}") - return loads_json(path.read_text(encoding="utf-8"), origin=str(path)) + return load_json(path) if scheme in ("http", "https"): if not allow_network: diff --git a/tests/unit/test_bundler_adapters.py b/tests/unit/test_bundler_adapters.py index 5ce9e10a12..854e60df3f 100644 --- a/tests/unit/test_bundler_adapters.py +++ b/tests/unit/test_bundler_adapters.py @@ -104,6 +104,18 @@ def test_fetch_rejects_malformed_source_url_cleanly(url): fetcher(_source(url)) +@pytest.mark.parametrize("use_file_url", [False, True], ids=["path", "file-url"]) +def test_local_catalog_decode_errors_are_wrapped(tmp_path, use_file_url): + catalog_path = tmp_path / "catalog.json" + catalog_path.write_bytes(b"\xff\xfe") + url = catalog_path.as_uri() if use_file_url else str(catalog_path) + + fetcher = adapters.make_catalog_fetcher(allow_network=False) + + with pytest.raises(BundlerError, match="Could not read"): + fetcher(_source(url)) + + def test_builtin_community_catalog_fetches_repository_catalog_online(monkeypatch): captured: dict = {}