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

  1. Wrap sync Router usage: with adapter: adapter.completion(...)
  2. Keep the adapter instance used inside the with-block scope for the whole call/stream duration
  3. If you already use a with-block, ensure completion() is not called after exit (move it inside)
  4. 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

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


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)