{"record":{"id":"6f5b9e882323f440","repo":"JuliusBrussee/caveman","slug":"closed-6f5b9e","errorCode":"closed","errorMessage":"closed","messagePattern":"closed","errorType":"error_code","errorClass":"MiddlewareError","httpStatus":null,"severity":"error","filePath":"packages/sdk/python/caveman_cloud/middleware/runtime.py","lineNumber":99,"sourceCode":"        self._url = url\n        self._connections: set[http.client.HTTPConnection] = set()\n        self._caps: dict | None = None\n        self._bindings: weakref.WeakKeyDictionary[RecoveryBinding, tuple] = weakref.WeakKeyDictionary()\n        self._lock = threading.RLock()\n        self._slots = threading.BoundedSemaphore(16)\n        self._closed = False\n        self._failures = 0\n        self._open_until = 0.0\n        self._receipt_pool: ThreadPoolExecutor | None = None\n        self._receipt_slots = threading.BoundedSemaphore(16)\n        self._async_view = None\n\n    def as_async(self):\n        \"\"\"Cached bounded async view; close() releases its owned worker pool.\"\"\"\n        from .async_runtime import AsyncMiddlewareRuntime\n        with self._lock:\n            if self._closed:\n                raise MiddlewareError(\"closed\")\n            if self._async_view is None or self._async_view._closed:\n                self._async_view = AsyncMiddlewareRuntime.from_sync(self)\n            return self._async_view\n\n    def ready(self) -> dict:\n        if self.mode == \"off\":\n            raise MiddlewareError(\"off\")\n        value = validate.capabilities(self._http(\"capabilities\", None, self.deadline_ms / 1000))\n        with self._lock:\n            if self._closed:\n                raise MiddlewareError(\"closed\")\n            self._caps = value\n        return copy.deepcopy(value)\n\n    def preflight(self) -> PreflightReport:\n        \"\"\"Nonthrowing discovery, even in strict mode; sends no candidate content.\"\"\"\n        if self.mode == \"off\":\n            return _preflight_report(self.mode, \"disabled\")","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/python/caveman_cloud/middleware/runtime.py#L81-L117","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()."],"exampleFix":"// before\nview = runtime.as_async()  # after runtime.close() -> MiddlewareError: closed\n// after\nif not runtime._closed:  # or keep a single owner responsible for close\n    view = runtime.as_async()\nelse:\n    runtime = MiddlewareRuntime(...)\n    view = runtime.as_async()","handlingStrategy":"try-catch","validationCode":"def as_async_safe(rt):\n    if getattr(rt, \"_closed\", False):\n        return None\n    return rt.as_async()","typeGuard":"def runtime_open(rt) -> bool:\n    return not getattr(rt, \"_closed\", True)","tryCatchPattern":"try:\n    view = rt.as_async()\nexcept MiddlewareError as e:\n    if str(e) == \"closed\":\n        view = None\n    else:\n        raise","preventionTips":["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."],"tags":["lifecycle","closed-runtime","python"],"backgroundTag":"invalid-state-transition","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}