python/cpython · error · TypeError

skip_file_prefixes must be a tuple of strs.

Error message

skip_file_prefixes must be a tuple of strs.

What it means

Raised by warnings.warn (pure-Python fallback path) when skip_file_prefixes is not a tuple. This keyword, used to attribute warnings to the first frame outside given library prefixes, is required to be a tuple because the performance-sensitive C implementation iterates it without type dispatch; lists, sets, or None are rejected with TypeError.

Source

Thrown at Lib/_py_warnings.py:482

# Code typically replaced by _warnings
def warn(message, category=None, stacklevel=1, source=None,
         *, skip_file_prefixes=()):
    """Issue a warning, or maybe ignore it or raise an exception."""
    # Check if message is already a Warning object
    if isinstance(message, Warning):
        category = message.__class__
    # Check category argument
    if category is None:
        category = UserWarning
    elif not isinstance(category, type):
        raise TypeError(f"category must be a Warning subclass, not "
                        f"'{type(category).__name__}'")
    elif not issubclass(category, Warning):
        raise TypeError(f"category must be a Warning subclass, not "
                        f"class '{category.__name__}'")
    if not isinstance(skip_file_prefixes, tuple):
        # The C version demands a tuple for implementation performance.
        raise TypeError('skip_file_prefixes must be a tuple of strs.')
    if skip_file_prefixes:
        stacklevel = max(2, stacklevel)
    # Get context information
    try:
        if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
            # If frame is too small to care or if the warning originated in
            # internal code, then do not try to hide any frames.
            frame = sys._getframe(stacklevel)
        else:
            frame = sys._getframe(1)
            # Look for one frame less since the above line starts us off.
            for x in range(stacklevel-1):
                frame = _next_external_frame(frame, skip_file_prefixes)
                if frame is None:
                    raise ValueError
    except ValueError:
        globals = sys.__dict__
        filename = "<sys>"

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass a tuple: skip_file_prefixes=(str(package_path), ...)
  2. Convert at the boundary: tuple(skip_file_prefixes) in wrappers
  3. Omit the keyword entirely when you don't need frame skipping

Example fix

# before
warnings.warn('deprecated', DeprecationWarning,
            skip_file_prefixes=[str(pkg_dir)])  # list -> TypeError

# after
warnings.warn('deprecated', DeprecationWarning,
            skip_file_prefixes=(str(pkg_dir),))
Defensive patterns

Strategy: type-guard

Validate before calling

def warn_external(message, category=Warning, *, prefixes=()):
    import warnings
    prefixes = tuple(prefixes) if prefixes is not None else ()
    return warnings.warn(message, category, skip_file_prefixes=prefixes)

Type guard

def is_str_tuple(v) -> bool:
    return isinstance(v, tuple) and all(isinstance(p, str) for p in v)

Prevention

When it happens

Trigger: warnings.warn('msg', DeprecationWarning, skip_file_prefixes=[site_packages]) — passing a list; skip_file_prefixes=None; a generator or set passed by generic wrapper code that forwards *args/**kwargs.

Common situations: Callers naturally writing a list literal; libraries exposing a variadic warn wrapper that forwards collections verbatim; default mutable-argument style leading to a list default.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/90d9839f2e0f4220. Report an issue: GitHub.