From 6d77b4a0994d699cdde41a9d5ae643f335ec61df Mon Sep 17 00:00:00 2001 From: Andrew Chen <48723787+chuenchen309@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:28:27 +0800 Subject: [PATCH] fix(integrations): catch OverflowError on a `priority: .inf` in add/remove (#3589) IntegrationCatalog.add_catalog and remove_catalog re-validate the existing catalog entries' priorities inline, separately from the base loader. Both did `int(raw_priority)` under `except (TypeError, ValueError)`, so a `priority: .inf` (float('inf')) raised OverflowError: add_catalog leaked a raw traceback instead of IntegrationValidationError, and remove_catalog crashed while building the display order. Add OverflowError to both handlers, matching the base loader (#3525) and the workflow/step loaders (#3526). add_catalog now raises IntegrationValidationError; remove_catalog falls back to positional order like the other non-integer priorities. Co-authored-by: Claude Opus 4.8 --- src/specify_cli/integrations/catalog.py | 6 ++- .../integrations/test_integration_catalog.py | 51 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/specify_cli/integrations/catalog.py b/src/specify_cli/integrations/catalog.py index aba5877d8..c9797934b 100644 --- a/src/specify_cli/integrations/catalog.py +++ b/src/specify_cli/integrations/catalog.py @@ -429,7 +429,8 @@ class IntegrationCatalog(CatalogStackBase): ) try: normalized_priority = int(raw_priority) - except (TypeError, ValueError): + except (TypeError, ValueError, OverflowError): + # OverflowError: int(float("inf")) — a ``priority: .inf``. raise IntegrationValidationError( f"Invalid catalog entry at index {idx} in {config_path}: " f"'priority' must be an integer, got " @@ -537,7 +538,8 @@ class IntegrationCatalog(CatalogStackBase): else: try: priority = int(raw_priority) - except (TypeError, ValueError): + except (TypeError, ValueError, OverflowError): + # OverflowError: int(float("inf")) — a ``priority: .inf``. priority = yaml_idx + 1 priority_pairs.append((priority, yaml_idx)) if not priority_pairs: diff --git a/tests/integrations/test_integration_catalog.py b/tests/integrations/test_integration_catalog.py index a0323b3ea..3ab9c3612 100644 --- a/tests/integrations/test_integration_catalog.py +++ b/tests/integrations/test_integration_catalog.py @@ -922,6 +922,57 @@ class TestCatalogSourceManagement: assert str(cfg_path) in message assert "expected a mapping" in message + def test_add_catalog_rejects_inf_priority_in_existing_entry( + self, tmp_path, monkeypatch + ): + # ``priority: .inf`` loads as float('inf'); int() on it raises + # OverflowError, which used to escape the IntegrationValidationError + # contract as a raw traceback (github/spec-kit#3526 fixed the sibling + # workflow/step loaders the same way). + self._isolate(tmp_path, monkeypatch) + cfg_path = tmp_path / ".specify" / "integration-catalogs.yml" + cfg_path.write_text( + yaml.dump( + { + "catalogs": [ + { + "url": "https://a.example.com/catalog.json", + "priority": float("inf"), + } + ] + } + ), + encoding="utf-8", + ) + cat = IntegrationCatalog(tmp_path) + with pytest.raises( + IntegrationValidationError, match="must be an integer" + ): + cat.add_catalog("https://new.example.com/catalog.json") + + def test_remove_catalog_tolerates_inf_priority(self, tmp_path, monkeypatch): + # Building the remove display order must not crash on a ``priority: + # .inf`` entry; it falls back to positional order like the other + # non-integer priorities do. + self._isolate(tmp_path, monkeypatch) + cfg_path = tmp_path / ".specify" / "integration-catalogs.yml" + cfg_path.write_text( + yaml.dump( + { + "catalogs": [ + { + "url": "https://a.example.com/catalog.json", + "priority": float("inf"), + }, + {"url": "https://b.example.com/catalog.json", "priority": 2}, + ] + } + ), + encoding="utf-8", + ) + cat = IntegrationCatalog(tmp_path) + cat.remove_catalog(0) # must not raise OverflowError + def test_add_catalog_skips_blank_url_entries(self, tmp_path, monkeypatch): self._isolate(tmp_path, monkeypatch) cfg_path = tmp_path / ".specify" / "integration-catalogs.yml"