JuliusBrussee/caveman · error · MiddlewareError
closed
closed
Error message
closed
What it means
MiddlewareRuntime.as_async() raises MiddlewareError("closed") when the sync runtime has already been shut down via close(). Once closed, the cached bounded async view can no longer be created or handed out. The library treats any use of a closed runtime as a programming error rather than silently recreating resources.
Solutions
- Obtain the async view via as_async() before closing the runtime, and keep the runtime alive as long as the async view is needed.
- Create a new MiddlewareRuntime instance instead of reusing the closed one.
- Guard call sites with a runtime.is-closed check or track lifecycle in application code before calling as_async().
Example fix
// before
view = runtime.as_async() # after runtime.close() -> MiddlewareError: closed
// after
if not runtime._closed: # or keep a single owner responsible for close
view = runtime.as_async()
else:
runtime = MiddlewareRuntime(...)
view = runtime.as_async() Defensive patterns
Strategy: try-catch
Validate before calling
def as_async_safe(rt):
if getattr(rt, "_closed", False):
return None
return rt.as_async() Type guard
def runtime_open(rt) -> bool:
return not getattr(rt, "_closed", True) Try / catch
try:
view = rt.as_async()
except MiddlewareError as e:
if str(e) == "closed":
view = None
else:
raise Prevention
- Fetch the async view once at startup, before any shutdown path can run.
- Keep a single owner responsible for calling close(); never close while request handlers are live.
- Treat `with` block exit as terminal: do not use the runtime afterwards.
When it happens
Trigger: Calling as_async() after MiddlewareRuntime.close() (or after exiting a `with` block, since __exit__ calls close()), or calling as_async() when the previously created async view was itself closed and the parent runtime is closed.
Common situations: Request handlers outliving application shutdown; context managers exited before background tasks fetch the async view; double-close race where one thread closes while another lazily builds the async view.
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
- closed
- Create a separate Caveman Strands bundle for each agent
- A dynamic Strands scope requires the native agent…
- Agno middleware requires a nonempty native session_id
- Agno scope resolver must return a Caveman Scope
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/6f5b9e882323f440.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/python/caveman_cloud/middleware/runtime.py:99
self._url = url
self._connections: set[http.client.HTTPConnection] = set()
self._caps: dict | None = None
self._bindings: weakref.WeakKeyDictionary[RecoveryBinding, tuple] = weakref.WeakKeyDictionary()
self._lock = threading.RLock()
self._slots = threading.BoundedSemaphore(16)
self._closed = False
self._failures = 0
self._open_until = 0.0
self._receipt_pool: ThreadPoolExecutor | None = None
self._receipt_slots = threading.BoundedSemaphore(16)
self._async_view = None
def as_async(self):
"""Cached bounded async view; close() releases its owned worker pool."""
from .async_runtime import AsyncMiddlewareRuntime
with self._lock:
if self._closed:
raise MiddlewareError("closed")
if self._async_view is None or self._async_view._closed:
self._async_view = AsyncMiddlewareRuntime.from_sync(self)
return self._async_view
def ready(self) -> dict:
if self.mode == "off":
raise MiddlewareError("off")
value = validate.capabilities(self._http("capabilities", None, self.deadline_ms / 1000))
with self._lock:
if self._closed:
raise MiddlewareError("closed")
self._caps = value
return copy.deepcopy(value)
def preflight(self) -> PreflightReport:
"""Nonthrowing discovery, even in strict mode; sends no candidate content."""
if self.mode == "off":
return _preflight_report(self.mode, "disabled")View on GitHub (pinned to 3ee70a1026)