python/cpython · error · TypeError
category must be a Warning subclass, not class '{category.__
Error message
category must be a Warning subclass, not class '{category.__name__}' What it means
Raised by the pure-Python warnings.warn fallback when category is a class but does not subclass Warning. warn() instantiates category(message) to build the Warning object, so arbitrary exception classes (ValueError, Exception, custom error hierarchies) are rejected with TypeError naming the offending class.
Source
Thrown at Lib/_py_warnings.py:478
frame = frame.f_back
return frame
# 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:View on GitHub (pinned to bc6749cc3b)
Solutions
- Define the warning as a Warning subclass: class MyWarning(UserWarning): ... and pass that
- Pick a built-in category that fits (UserWarning, DeprecationWarning, RuntimeWarning, FutureWarning, ResourceWarning)
- If you actually want an exception, use raise, not warnings.warn
Example fix
# before
class ConfigError(Exception): ...
warnings.warn('unknown key', ConfigError) # TypeError: not a Warning subclass
# after
class ConfigWarning(UserWarning): ...
warnings.warn('unknown key', ConfigWarning) Defensive patterns
Strategy: type-guard
Validate before calling
def as_warning_category(cat):
if isinstance(cat, type) and issubclass(cat, Warning):
return cat
raise TypeError(f'{cat!r} is not a Warning subclass; derive it from Warning/UserWarning')
import warnings
warnings.warn('deprecated', as_warning_category(cfg_category)) Type guard
def is_warning_class(cat) -> bool:
return isinstance(cat, type) and issubclass(cat, Warning) Prevention
- Model 'warnable conditions' as Warning subclasses, separate from exception hierarchies
- Common mapping: recoverable deprecation -> DeprecationWarning; runtime concern -> RuntimeWarning
- If the condition should abort, raise the exception instead of warning with it
When it happens
Trigger: warnings.warn('bad input', ValueError); warnings.warn('retrying', MyException) where MyException extends Exception; mixing up exception-raising and warning-issuing paths in shared helper code.
Common situations: Libraries that 'sometimes warn, sometimes raise' and pass the same exception class to both; new team members assuming warn accepts exceptions; refactoring raises into warns without changing the class; custom exceptions meant to double as warning categories.
Related errors
- category must be a Warning subclass
- category must be a Warning subclass, not '{type(category).__
- invalid action: {action!r}
- message must be a string
- module must be a string
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/5c49bb8b99ee60b1.
Report an issue: GitHub.