Skip to content

[BUG] After upgrading from a yanked release, every run reports the new version as yanked for 24 hours #9134

Open

Description

Description

The PyPI check keeps one cache file (version_cache.json) with the latest version and whether the installed version is yanked.
get_latest_version_from_pypi trusts any cache younger than 24 hours that has a current_version key, whichever version wrote it.

is_current_version_yanked notices the cache was written for another version and calls get_latest_version_from_pypi to refresh it.
That call returns straight from the same cache, so nothing is fetched, and the old yanked flag is read back.

After upgrading from a yanked release, the console prints the red "Yanked Version" panel on every run for up to 24 hours.
It names the new version and shows the old release's reason.

Steps to Reproduce

  1. Run a yanked crewAI release once, so the cache holds current_version_yanked: true.
  2. Upgrade to a good release (uv sync --upgrade-package crewai).
  3. Run any crew or flow with the console output on.

Without installing anything, the same state:

import json, tempfile
from datetime import datetime
from pathlib import Path
from unittest.mock import patch, MagicMock
import crewai_core.version as v

cache = Path(tempfile.mkdtemp()) / "version_cache.json"
cache.write_text(json.dumps({
    "version": "2.0.0", "timestamp": datetime.now().isoformat(),
    "current_version": "1.0.0", "current_version_yanked": True,
    "current_version_yanked_reason": "bad release"}))
releases = {"releases": {"1.0.0": [{"yanked": True, "yanked_reason": "bad release"}],
                         "2.0.0": [{"yanked": False}]}}
resp = MagicMock()
resp.__enter__.return_value.read.return_value = json.dumps(releases).encode()
with patch.object(v, "_get_cache_file", return_value=cache), \
     patch.object(v, "get_crewai_version", return_value="2.0.0"), \
     patch.object(v.request, "urlopen", return_value=resp) as urlopen:
    print(v.is_current_version_yanked(), "urlopen calls:", urlopen.call_count)

Expected behavior

2.0.0 is not yanked, so the result is (False, "").
The cache from 1.0.0 is not used, and PyPI is queried once to rewrite it.

Screenshots/Code snippets

Output on main:

(True, 'bad release') urlopen calls: 0

The panel comes from ConsoleFormatter (crewai/events/utils/console_formatter.py):

Version 2.0.0 has been yanked from PyPI.
Reason: bad release

To update, run: uv sync --upgrade-package crewai

Operating System

Windows 11

Python Version

3.13 (the form lists 3.10 to 3.12, nothing in the code depends on it)

crewAI Version

1.15.35 (main at 6bd60b2).

crewAI Tools Version

1.15.35

Virtual Environment

Venv (uv)

Evidence

Tests added on the fix branch, run against unfixed code:

FAILED test_version.py::TestVersionCacheAfterUpgrade::test_upgrading_from_a_yanked_version_clears_the_yanked_flag   assert True is False
FAILED test_version.py::TestVersionCacheAfterUpgrade::test_cache_written_for_another_version_is_not_used_offline    assert True is False
FAILED test_version.py::TestVersionCacheAfterUpgrade::test_latest_version_is_refetched_for_a_new_installed_version  assert '2.0.0' == '3.0.0'

The existing test_triggers_fetch_on_stale_cache hides this: it mocks get_latest_version_from_pypi, so the cache short-circuit never runs.

Possible Solution

In get_latest_version_from_pypi, use the cache only when cache_data["current_version"] == get_crewai_version().
An upgrade then fetches once and rewrites the file.
In is_current_version_yanked, return (False, "") when the fetch failed and the cache still belongs to another version.

Additional context

The cache also drives the "update available" check, so it is read the same way there. The yanked panel is the visible symptom.
Searches run (no match for this bug):

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions