Skip to content

Commit 828259f

Browse files
joaomdmouraclaude
andcommitted
fix(skills): reject a blank version pin instead of floating to latest
A blank `version` passed to `download_skill` read as "unpinned" and quietly resolved the latest version, which is not what a caller supplying one asked for — and it disagreed with `parse_skill_ref`, which already rejects empty pins. Not reachable through `resolve_registry_ref` or the Agent Repository auto-pinning, both of which only ever pass a non-empty version. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 6d12fff commit 828259f

2 files changed

Lines changed: 24 additions & 0 deletions

File tree

lib/crewai/src/crewai/skills/registry.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,23 @@ def download_skill(
279279
280280
Returns:
281281
The downloaded Skill at INSTRUCTIONS level.
282+
283+
Raises:
284+
ValueError: If *version* is given but blank.
282285
"""
283286
from crewai.skills.loader import activate_skill
284287
from crewai.skills.parser import load_skill_metadata
285288
from crewai.utilities.agent_utils import resolve_plus_response
286289

290+
if version is not None:
291+
# A blank pin would otherwise read as "unpinned" and quietly float to
292+
# the latest version, which is not what a caller passing one asked for.
293+
version = version.strip()
294+
if not version:
295+
raise ValueError(
296+
"A pinned skill version must be non-empty; omit it to fetch the latest."
297+
)
298+
287299
ref = str(SkillRef(org=org, name=name, version=version))
288300

289301
try:

lib/crewai/tests/skills/test_registry.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,18 @@ def get_skill(self, org: str, name: str) -> MagicMock:
438438

439439
assert calls == [("acme", "my-skill")]
440440

441+
@pytest.mark.parametrize("version", ["", " "], ids=["empty", "whitespace"])
442+
def test_rejects_a_blank_pin_rather_than_floating_to_latest(
443+
self, monkeypatch: pytest.MonkeyPatch, version: str
444+
) -> None:
445+
installed = _stub_api("my-skill")
446+
_install_client(monkeypatch, installed)
447+
448+
with pytest.raises(ValueError, match="must be non-empty"):
449+
download_skill("acme", "my-skill", version=version)
450+
451+
installed.get_skill.assert_not_called()
452+
441453
def test_reports_the_pinned_ref_when_the_download_fails(
442454
self, monkeypatch: pytest.MonkeyPatch
443455
) -> None:

0 commit comments

Comments
 (0)