diff --git a/src/specify_cli/events.py b/src/specify_cli/events.py index 98e49aee36..26bbaaa0f2 100644 --- a/src/specify_cli/events.py +++ b/src/specify_cli/events.py @@ -737,8 +737,10 @@ def resolve_events( if override_file.exists(): try: override = yaml.safe_load(override_file.read_text(encoding="utf-8")) or {} - except yaml.YAMLError: - logger.warning("Could not parse %s; ignoring override", override_file) + except (OSError, UnicodeError, yaml.YAMLError): + logger.warning( + "Could not read or parse %s; ignoring override", override_file + ) override = {} integrations = override.get("integrations", {}) if isinstance(override, dict) else {} if isinstance(integrations, dict) and integration_key in integrations: diff --git a/tests/integrations/test_events.py b/tests/integrations/test_events.py index 659ae48599..c9b9678c76 100644 --- a/tests/integrations/test_events.py +++ b/tests/integrations/test_events.py @@ -128,6 +128,23 @@ def test_layer2_empty_events_disables(self, tmp_path): ) assert result == {} + def test_unreadable_yaml_override_keeps_prior_layers(self, tmp_path): + """An unreadable override is ignored like malformed YAML.""" + override_file = tmp_path / ".specify" / "integration-events.yml" + override_file.parent.mkdir(parents=True, exist_ok=True) + override_file.write_bytes(b"\xff\xfe") + + result = resolve_events( + "claude", + {"events": {"post_tool_use": {"command": "speckit.tdd.validate"}}}, + tmp_path, + None, + ) + + assert result == { + "post_tool_use": [{"command": "speckit.tdd.validate"}] + } + def test_no_config_no_events(self, tmp_path): """Safe fallback with empty config/options.""" result = resolve_events("claude", None, tmp_path, None)