harry0703/MoneyPrinterTurbo · error · ValueError

max_age_days must be a positive integer or None

Error message

max_age_days must be a positive integer or None

What it means

ValueError from _validate_max_age_days in the cache manager: max_age_days must be None or a positive int, but the caller passed a non-int, a bool, zero, or a negative number. The explicit isinstance(max_age_days, bool) check rejects True/False even though bool subclasses int. Validation runs even when the cache directory is empty, so bad parameters fail deterministically instead of silently passing.

Source

Thrown at app/services/cache_manager.py:120

    entry: _VideoCacheEntry,
    max_age_days: int | None,
    now: float,
) -> bool:
    if max_age_days is None:
        return True
    return entry.mtime < now - max_age_days * _SECONDS_PER_DAY


def _validate_max_age_days(max_age_days: int | None) -> None:
    """即使缓存目录为空,也应稳定拒绝无效清理参数。"""
    if max_age_days is None:
        return
    if (
        isinstance(max_age_days, bool)
        or not isinstance(max_age_days, int)
        or max_age_days <= 0
    ):
        raise ValueError("max_age_days must be a positive integer or None")


def get_video_cache_stats(max_age_days: int | None = None) -> VideoCacheStats:
    """
    统计全部缓存,或预览修改时间早于指定天数的可清理缓存。

    ``max_age_days=None`` 表示全部缓存。统计过程只读取目录项的大小和修改时间,
    不读取视频内容,因此即使缓存总容量很大也不会产生与容量成比例的 I/O。
    """

    _validate_max_age_days(max_age_days)
    now = time.time()
    file_count = 0
    total_size = 0
    oldest_mtime = None
    newest_mtime = None

    for entry in _iter_video_cache_entries():

View on GitHub (pinned to 1f9f19c202)

Solutions

  1. Pass None for 'all caches' and a positive int (e.g. 30) for age-based stats/cleanup.
  2. Coerce user input at the boundary: days = int(str(days)) and validate days > 0, or map falsy to None.
  3. Never rely on True/False — booleans are deliberately rejected.

Example fix

// before
get_video_cache_stats(max_age_days=request.args.get('days', type=float))  # 7.0 -> ValueError

// after
days = request.args.get('days', type=int)
get_video_cache_stats(max_age_days=days if days and days > 0 else None)
Defensive patterns

Strategy: type-guard

Validate before calling

def normalize_max_age_days(value):
    if value is None:
        return None
    days = int(value)
    if isinstance(value, bool) or days <= 0:
        raise ValueError('max_age_days must be a positive integer or None')
    return days

Type guard

def is_valid_max_age_days(value) -> bool:
    """True only for None or positive non-bool ints."""
    if value is None:
        return True
    return not isinstance(value, bool) and isinstance(value, int) and value > 0

Try / catch

try:
    get_video_cache_stats(max_age_days=days)
except ValueError as e:
    if 'max_age_days' in str(e):
        days = None  # fall back to full stats
        get_video_cache_stats(max_age_days=days)

Prevention

When it happens

Trigger: Calling get_video_cache_stats(max_age_days=...), cleanup, or any API that funnels into _validate_max_age_days with 0, -1, 7.0, '7', or True. WebUI form values parsed as strings/floats are the classic source.

Common situations: Form/query params arriving as strings ('30') and being passed through unconverted; floats from division (days*1.0); using True as a truthy 'purge everything' flag; 0 intended to mean 'all' instead of None.

Related errors


AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14). Data as JSON: /api/errors/a456ec9c12a1d87e. Report an issue: GitHub.