JuliusBrussee/caveman · error · MiddlewareError

unsupported_sync_router_strict

unsupported_sync_router_strict

Error message

unsupported_sync_router_strict

What it means

Sync litellm.Router completion runs logging callbacks that swallow exceptions, so in strict mode a middleware failure could not abort the request — the strict guarantee would be silently broken. The adapter rejects this combination up front (before the Router dispatches) by reporting 'unsupported_sync_router_strict' and raising MiddlewareError with that code.

Solutions

  1. Switch sync Router calls to await acompletion(...) (async path), keeping strict mode on
  2. Disable strict mode on the MiddlewareRuntime if you must keep sync Router calls
  3. Use a plain litellm client (non-Router) for sync completion calls
  4. Gate strict mode per-environment so sync-Router deployments run non-strict

Example fix

// before
# runtime = MiddlewareRuntime(strict=True); client = litellm.Router(...)
adapter.completion(scope=scope, model=..., messages=...)  # MiddlewareError

// after
await adapter.acompletion(scope=scope, model=..., messages=...)  # strict-safe
Defensive patterns

Strategy: try-catch

Validate before calling

from litellm import Router
if isinstance(adapter.client, Router) and getattr(adapter.runtime, "strict", False):
    raise RuntimeError("sync Router completion is unsupported with strict=True; use acompletion or disable strict")

Type guard

def sync_router_strict_supported(adapter) -> bool:
    from litellm import Router
    return not (isinstance(adapter.client, Router) and adapter.runtime.strict)

Try / catch

try:
    return adapter.completion(scope=scope, **kwargs)
except MiddlewareError as e:
    if e.code == "unsupported_sync_router_strict":
        return await adapter.acompletion(scope=scope, **kwargs)

Prevention

When it happens

Trigger: Calling completion() while self.client is a litellm.Router, method is 'completion', and self.runtime.strict is True.

Common situations: Enabling strict mode in production config while still using the sync Router for load balancing/fallbacks; flipping runtime.strict on without migrating sync Router traffic to acompletion.

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


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/8286185529bb0c6f. Report an issue: GitHub.

Appendix: source

Thrown at packages/middleware/python/caveman_middleware/litellm.py:308

            return function(**kwargs)
        passive_reason = self._passive_reason(method, kwargs)
        if passive_reason:
            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:

View on GitHub (pinned to 3ee70a1026)