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
- Pass a tuple: skip_file_prefixes=(str(package_path), ...)
- Convert at the boundary: tuple(skip_file_prefixes) in wrappers
- 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
- Always use a tuple literal (item,) for skip_file_prefixes
- Convert collections at wrapper boundaries with tuple(...)
- Remember stacklevel is auto-raised when prefixes are supplied; don't double-adjust
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
- invalid action: {action!r}
- message must be a string
- category must be a Warning subclass
- module must be a string
- lineno must be an int
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/90d9839f2e0f4220.
Report an issue: GitHub.