JuliusBrussee/caveman · error · RuntimeError
not_registered
not_registered
Error message
Use CavemanLiteLLM as a context manager while sync Router calls and streams are active
What it means
Sync Router calls and streams rely on callbacks the adapter registers when the CavemanLiteLLM context manager is entered (_registrations counter). If completion() runs on a Router with zero registrations, the adapter reports 'not_registered' and raises RuntimeError telling you to use it as a context manager.
Solutions
- Wrap sync Router usage: with adapter: adapter.completion(...)
- Keep the adapter instance used inside the with-block scope for the whole call/stream duration
- If you already use a with-block, ensure completion() is not called after exit (move it inside)
- Check that __enter__ actually ran — e.g. no early return between construction and use
Example fix
// before
adapter = CavemanLiteLLM(runtime=runtime, client=router)
adapter.completion(scope=scope, ...) # RuntimeError: not_registered
// after
with CavemanLiteLLM(runtime=runtime, client=router) as adapter:
adapter.completion(scope=scope, ...) Defensive patterns
Strategy: try-catch
Validate before calling
if getattr(adapter, "_registrations", 1) == 0:
raise RuntimeError("enter 'with adapter:' before sync Router completion calls") Type guard
def router_context_active(adapter) -> bool:
return getattr(adapter, "_registrations", 0) > 0 Try / catch
try:
return adapter.completion(scope=scope, **kwargs)
except RuntimeError as e:
if "context manager" in str(e):
with adapter:
return adapter.completion(scope=scope, **kwargs) Prevention
- Always use `with adapter:` around sync Router completion and streams
- Keep the completion call inside the same function that opens the context manager
- Enforce the pattern with a team lint rule or wrapper helper
- Never store a bare adapter for direct calls; store a factory that returns a context-managed session
When it happens
Trigger: Calling completion() on a litellm.Router-backed adapter without entering `with adapter:` (or after the context manager has exited, when _registrations is back to 0).
Common situations: Long-lived adapter stored on a module/class and called directly; entering the context manager in one function and calling completion in another; forgetting the with-block in scripts and notebooks.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- cave artifact response missing artifact_id
- Install caveman-middleware[litellm] to use the LiteLLM…
- LiteLLM metadata must be a native dictionary
- LiteLLM scope must be a trusted Caveman Scope
- Recovery executor belongs to another runtime or scope
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/e1be7ce9f3869299.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/litellm.py:311
attempt = Attempt(self.runtime, None, str(uuid.uuid4()), str(uuid.uuid4()), passive=True,
reason=passive_reason, adapter="litellm")
attempt.observe("dispatch_intent")
token = owner.set(attempt)
try:
return function(**kwargs)
finally:
owner.reset(token)
if not isinstance(self.runtime, MiddlewareRuntime):
raise TypeError("Synchronous LiteLLM calls require MiddlewareRuntime")
router = isinstance(self.client, native.Router) and method == "completion"
if router and self.runtime.strict:
# Logging callbacks swallow their exceptions. Reject this capability
# before calling the Router so strict failure cannot dispatch a request.
self._report("unsupported_sync_router_strict")
raise MiddlewareError("unsupported_sync_router_strict")
if router and self._registrations == 0:
self._report("not_registered")
raise RuntimeError("Use CavemanLiteLLM as a context manager while sync Router calls and streams are active")
with self._activation(scope, kwargs, method) as (params, request):
if router:
token = _sync_router.set(self)
try:
return function(**params)
finally:
_sync_router.reset(token)
# Direct sync SDK calls already name the selected provider/model.
session = self._session(request, method, self.runtime) if self.client is native else None
token = owner.set(None)
try:
body, attempt = session.prepare(params) if session else (params, None)
finally:
owner.reset(token)
if attempt:
body = {**body, "litellm_call_id": str(uuid.uuid4())}
attempt.observe("dispatch_intent")
self._save_attempt(body, attempt)View on GitHub (pinned to 3ee70a1026)