roboflow/supervision · error · AttributeError
unreadable attribute
Error message
unreadable attribute
What it means
Raised by classproperty.__get__ in supervision.utils.internal when a classproperty is accessed but its fget getter is None. This mirrors Python's built-in property behavior for properties defined without a getter. In practice it signals a misconstructed classproperty, typically from subclassing or dynamically creating one without a getter function.
Source
Thrown at src/supervision/utils/internal.py:166
Args:
The function that is called when the property is accessed.
"""
self.fget = fget
def __get__(self, owner_self: Any, owner_cls: type | None = None) -> T:
"""
Override the __get__ method to return the result of the function call.
Args:
owner_self: The instance through which the attribute was accessed, or None.
Irrelevant for class properties.
owner_cls: The class through which the attribute was accessed.
Returns:
The result of calling the function stored in 'fget' with 'owner_cls'.
"""
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(owner_cls)
def get_instance_variables(instance: Any, include_properties: bool = False) -> set[str]:
"""
Get the public variables of a class instance.
Args:
instance: The instance of a class
include_properties: Whether to include properties in the result
Usage:
```pycon
>>> from supervision.utils.internal import get_instance_variables
>>> import numpy as np
>>> from supervision import Detections
>>> detections = Detections(xyxy=np.array([[1, 2, 3, 4]]))
>>> variables = get_instance_variables(detections)View on GitHub (pinned to 7f254d9784)
Solutions
- Define the classproperty with a getter: @classproperty def name(cls): ...
- If you were trying to assign to a classproperty, replace the whole object with a new classproperty(getter) instead of mutating.
- Audit custom code that constructs classproperty(...) directly and ensure the callable is passed.
Example fix
// before
prop = classproperty(None)
class A: x = prop
A.x # AttributeError
// after
class A:
@classproperty
def x(cls):
return 42
A.x # 42 Defensive patterns
Strategy: type-guard
Validate before calling
if isinstance(obj, classproperty) and obj.fget is None:
raise AttributeError('classproperty defined without getter') Type guard
def has_getter(prop: classproperty) -> bool:
return callable(getattr(prop, 'fget', None)) Try / catch
try:
value = MyClass.some_prop
except AttributeError as e:
if 'unreadable attribute' in str(e):
logger.error('MyClass.some_prop is a classproperty without fget')
raise Prevention
- Always define classproperty via the @classproperty decorator with a body.
- Do not assign bare classproperty objects in metaprogramming; always pass a getter.
When it happens
Trigger: Accessing SomeClass.some_classproperty where the classproperty was created with fget=None — e.g. manually assigning classproperty(None), or a decorator pipeline that dropped the getter. Reading the attribute on the class or any instance triggers it.
Common situations: Advanced metaprogramming that wraps or clones classproperty objects; copy-pasting the classproperty implementation and omitting the getter; trying to set a classproperty via attribute assignment (setter is not supported, leaving a broken object).
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/d40f35cb329e7026.
Report an issue: GitHub.