JuliusBrussee/caveman · error · AttributeError
Caveman recovery metadata is immutable
Error message
Caveman recovery metadata is immutable
What it means
_FrozenModel subclasses LlamaIndex's Pydantic BaseModel and sets a private flag after init; any subsequent attribute assignment on the instance raises AttributeError because recovery metadata objects must not be mutated after construction. This protects the bookkeeping state the caveman runtime relies on.
Solutions
- Treat the object as read-only; construct a new instance with the desired values instead of mutating
- Use object.__setattr__(obj, name, value) only if you explicitly must bypass the freeze (not recommended)
- If you own the wrapping code, extract needed values at construction time and keep your own mutable copy
Example fix
// before
recovery_meta.name = "renamed"
// after
new_meta = type(recovery_meta)(**{**recovery_meta.model_dump(), "name": "renamed"}) Defensive patterns
Strategy: validation
Validate before calling
def can_set(obj, name):
return not getattr(obj, "_caveman_frozen", False)
# check before assigning
if not can_set(recovery_meta, "name"):
recovery_meta = type(recovery_meta)(**{**recovery_meta.model_dump(), "name": new_name}) Type guard
def is_frozen(obj) -> bool:
return getattr(obj, "_caveman_frozen", False) Prevention
- Treat caveman-created metadata objects as read-only
- Copy via model_dump() when you need a modified version
- Avoid generic middleware that mutates tool/model attributes in place
When it happens
Trigger: Assigning any attribute (e.g. recovery_obj.some_field = x or recovery_obj.new_attr = 1) on a caveman-created recovery metadata/model instance after it has been constructed.
Common situations: Patching tool metadata in place, applying generic middleware that copies/rewrites attributes on LlamaIndex objects, or framework code that updates model fields post-init.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Caveman recovery tool is immutable
- Caveman delegates through public chat_with_tools methods
- Expected a native ToolSelection
- Expected an existing native LlamaIndex LLM
- Expected distinct native tools without caveman_retrieve
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2bd34a3739a6eb84.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/llama_index.py:95
_invocation = contextvars.ContextVar("caveman_llama_index_invocation", default=None)
class _RecoveryMetadata(ToolMetadata):
def get_parameters_dict(self):
# Default ToolMetadata removes additionalProperties from the schema.
return copy.deepcopy(RECOVERY_SCHEMA)
class _FrozenRecoveryMetadata(_RecoveryMetadata):
def __init__(self, **kwargs):
super().__init__(**kwargs)
object.__setattr__(self, "_caveman_frozen", True)
def __setattr__(self, name, value):
if getattr(self, "_caveman_frozen", False):
raise AttributeError("Caveman recovery metadata is immutable")
super().__setattr__(name, value)
class _FrozenFunctionTool(FunctionTool):
def __init__(self, **kwargs):
super().__init__(**kwargs)
object.__setattr__(self, "_caveman_frozen", True)
def __setattr__(self, name, value):
if getattr(self, "_caveman_frozen", False):
raise AttributeError("Caveman recovery tool is immutable")
super().__setattr__(name, value)
class _Recovery:
def __init__(self, runtime, scope):
self.runtime, self.scope = runtime, scope
View on GitHub (pinned to 3ee70a1026)