JuliusBrussee/caveman · error · TypeError
CrewAI requires a MiddlewareRuntime and a stable Scope per…
Error message
CrewAI requires a MiddlewareRuntime and a stable Scope per agent context
What it means
CavemanLLM's __init__ validates that the keyword arguments runtime and scope are instances of MiddlewareRuntime and Scope respectively. The middleware requires a runtime for interception and a stable per-agent Scope so recovery and usage can be keyed consistently; passing anything else raises this TypeError.
Solutions
- Construct runtime with the library's MiddlewareRuntime factory and pass that instance.
- Build a proper Scope object (one per agent context) rather than a string or dict.
- Check isinstance(runtime, MiddlewareRuntime) and isinstance(scope, Scope) before constructing the wrapper.
Example fix
// before wrapped = CavemanLLM(llm, runtime="on", scope="agent-1") // after from caveman_cloud.middleware import MiddlewareRuntime, Scope wrapped = CavemanLLM(llm, runtime=MiddlewareRuntime(...), scope=Scope(...))
Defensive patterns
Strategy: type-guard
Validate before calling
from caveman_cloud.middleware import MiddlewareRuntime, Scope
if not isinstance(runtime, MiddlewareRuntime) or not isinstance(scope, Scope):
raise TypeError("Pass MiddlewareRuntime and Scope instances") Type guard
def valid_runtime_scope(runtime, scope) -> bool:
from caveman_cloud.middleware import MiddlewareRuntime, Scope
return isinstance(runtime, MiddlewareRuntime) and isinstance(scope, Scope) Try / catch
try:
wrapped = CavemanLLM(llm, runtime=runtime, scope=scope)
except TypeError as e:
if "MiddlewareRuntime" in str(e):
raise ConfigError("Build runtime/scope with the caveman middleware factories") from e
raise Prevention
- Create runtime and scope via the library's factories, never ad-hoc dicts/strings.
- Give each agent context its own stable Scope instance.
- Add a small factory function that constructs runtime+scope so call sites can't pass wrong types.
- Assert types once at startup rather than per call.
When it happens
Trigger: Calling CavemanLLM(delegate, runtime=<not a MiddlewareRuntime>, scope=<not a Scope>) — e.g. runtime as a plain config dict or string mode, scope as a string identifier, or either argument omitted/None.
Common situations: Hand-rolling the runtime instead of using the library's factory; passing a scope name string where a Scope object is required; reusing a single global scope across agents instead of a stable per-agent context; upgrading caveman-middleware and old positional/keyword shapes no longer match.
Related errors
- Expected a Google Client and synchronous MiddlewareRuntime
- Expected an installed CrewAI BaseLLM
- Agno middleware requires a nonempty native session_id
- Agno scope resolver must return a Caveman Scope
- AutoGen requires a stable Caveman Scope for each agent or…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/bbe2af3e63c144e0.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/crewai.py:113
Register before creating the Agent, whose executor snapshots native hooks.
``close`` removes only this delegate's registrations. The application keeps
ownership of the native provider client and the Caveman runtime.
"""
llm_type: str = "caveman"
delegate: BaseLLM = Field(exclude=True, repr=False)
runtime: Any = Field(exclude=True, repr=False)
scope: Scope
closed: bool = Field(default=False, exclude=True)
_context: Any = PrivateAttr()
_hook: Any = PrivateAttr(default=None)
_completed: Any = PrivateAttr(default=None)
_recovery: Any = PrivateAttr(default=None)
def __init__(self, delegate, *, runtime, scope):
if not isinstance(delegate, BaseLLM):
raise TypeError("Expected an installed CrewAI BaseLLM")
if not isinstance(runtime, MiddlewareRuntime) or not isinstance(scope, Scope):
raise TypeError("CrewAI requires a MiddlewareRuntime and a stable Scope per agent context")
super().__init__(delegate=delegate, runtime=runtime, scope=scope, model=delegate.model,
provider=delegate.provider, stream=delegate.stream, stop=list(delegate.stop),
is_litellm=delegate.is_litellm)
self._context = contextvars.ContextVar(f"caveman_crewai_context_{id(self)}", default=None)
supported = matches_framework(("crewai", "1.15", "2"))
if not supported and runtime.mode != "off":
runtime.decline("unsupported_version")
if not supported or runtime.mode == "off":
return
reference = weakref.ref(self)
@on(InterceptionPoint.PRE_MODEL_CALL)
def before(context):
model = reference()
if model is not None and not model.closed and context.llm is model:
# Store only a weak executor reference. Aborted hooks and stale
# executor snapshots cannot retain complete message histories.
model._context.set(weakref.ref(context.executor) if context.executor is not None else None)View on GitHub (pinned to 3ee70a1026)