diff --git a/src/specify_cli/events.py b/src/specify_cli/events.py index 3469115d6e..ee65f951ae 100644 --- a/src/specify_cli/events.py +++ b/src/specify_cli/events.py @@ -1014,7 +1014,7 @@ def collect_extension_events(project_root: Path) -> ResolvedEvents: continue try: data = yaml.safe_load(ext_yml.read_text(encoding="utf-8")) or {} - except (UnicodeDecodeError, yaml.YAMLError): + except (OSError, UnicodeDecodeError, yaml.YAMLError): continue if not isinstance(data, dict): continue diff --git a/tests/integrations/test_events.py b/tests/integrations/test_events.py index 5dfc497b95..cf48411740 100644 --- a/tests/integrations/test_events.py +++ b/tests/integrations/test_events.py @@ -188,6 +188,22 @@ def test_non_utf8_manifest_skipped(self, tmp_path): assert collect_extension_events(tmp_path) == {} + def test_unreadable_manifest_skipped(self, tmp_path, monkeypatch): + ext_dir = tmp_path / ".specify" / "extensions" / "my-ext" + ext_dir.mkdir(parents=True) + manifest = ext_dir / "extension.yml" + manifest.write_text("events: {}\n", encoding="utf-8") + real_read_text = Path.read_text + + def unreadable(path, *args, **kwargs): + if path == manifest: + raise OSError("simulated read failure") + return real_read_text(path, *args, **kwargs) + + monkeypatch.setattr(Path, "read_text", unreadable) + + assert collect_extension_events(tmp_path) == {} + def test_event_command_ref_canonicalized_via_manifest(self, tmp_path): """R1: events are read from a validated ExtensionManifest, so an obsolete command ref (e.g. my-ext.boot) is canonicalized