Skip to content

Commit da98d4d

Browse files
inamdarmihircopybara-github
authored andcommitted
fix: report unreadable SKILL.md files to on_error
Merge #7760 list_skills_in_dir now passes any OSError raised while reading a skill's SKILL.md (for example a PermissionError) to on_error, or logs and skips that skill without it. Before, only FileNotFoundError was handled, so an unreadable SKILL.md raised out of the listing and the skills already collected were lost. A caller without on_error that relied on that exception now gets a warning and a result without that skill. PiperOrigin-RevId: 1010775984
1 parent f6325bc commit da98d4d

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

‎src/google/adk/skills/_utils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ def _list_skills_in_dir(
610610
f" name '{skill_id}'."
611611
)
612612
skills[skill_id] = frontmatter
613-
except (FileNotFoundError, ValueError, ValidationError) as e:
613+
except (OSError, ValueError, ValidationError) as e:
614614
if on_error is not None:
615615
on_error(skill_id, e)
616616
continue

‎tests/unittests/skills/test__utils.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,33 @@ def strict(skill_id, error):
716716
list_skills_in_dir(_skills_dir_with_one_invalid(tmp_path), on_error=strict)
717717

718718

719+
def test_list_skills_in_dir_reports_unreadable_skill_md_to_on_error(tmp_path):
720+
"""A SKILL.md that cannot be read is reported and other skills are kept."""
721+
skills_dir = tmp_path / "skills"
722+
for name in ("good", "blocked"):
723+
(skills_dir / name).mkdir(parents=True)
724+
(skills_dir / name / "SKILL.md").write_text(
725+
f"---\nname: {name}\ndescription: desc\n---\nbody"
726+
)
727+
real_read_text = pathlib.Path.read_text
728+
729+
def fake_read_text(self, *args, **kwargs):
730+
if self.parent.name == "blocked":
731+
raise PermissionError(13, "Permission denied", str(self))
732+
return real_read_text(self, *args, **kwargs)
733+
734+
failures = []
735+
with mock.patch.object(pathlib.Path, "read_text", fake_read_text):
736+
skills = list_skills_in_dir(
737+
skills_dir,
738+
on_error=lambda skill_id, error: failures.append((skill_id, error)),
739+
)
740+
741+
assert list(skills) == ["good"]
742+
assert [skill_id for skill_id, _ in failures] == ["blocked"]
743+
assert isinstance(failures[0][1], PermissionError)
744+
745+
719746
def test__load_skill_from_zip_bytes():
720747
"""Tests loading a skill directly from in-memory zip file bytes."""
721748

0 commit comments

Comments
 (0)