{"record":{"id":"e96d1dbcd2930df6","repo":"JuliusBrussee/caveman","slug":"closed","errorCode":"closed","errorMessage":"closed","messagePattern":"closed","errorType":"error_code","errorClass":"MiddlewareError","httpStatus":null,"severity":"error","filePath":"packages/sdk/python/caveman_cloud/middleware/async_runtime.py","lineNumber":118,"sourceCode":"\n    async def delete_session(self, scope):\n        return await self._submit(self._runtime.delete_session, scope)\n\n    async def aclose(self):\n        self._closed = True\n        if self._owns_runtime:\n            self._runtime.close()\n        await asyncio.to_thread(self._executor.shutdown, wait=True, cancel_futures=True)\n\n    async def __aenter__(self):\n        return self\n\n    async def __aexit__(self, *_):\n        await self.aclose()\n\n    async def _submit(self, function, *args, **kwargs):\n        if self._closed:\n            raise MiddlewareError(\"closed\")\n        if not self._slots.acquire(blocking=False):\n            raise MiddlewareError(\"capacity\")\n        context = contextvars.copy_context()\n        try:\n            future = self._executor.submit(context.run, functools.partial(function, *args, **kwargs))\n        except BaseException:\n            self._slots.release()\n            raise\n        # Release only when the underlying worker actually terminates. Releasing\n        # on coroutine cancellation would allow unlimited queued/running work.\n        future.add_done_callback(lambda _: self._slots.release())\n        return await asyncio.wrap_future(future)\n","sourceCodeStart":100,"sourceCodeEnd":131,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/python/caveman_cloud/middleware/async_runtime.py#L100-L131","documentation":"The async runtime's _submit refuses to run work after aclose() has been called: it raises MiddlewareError(\"closed\"). This protects against submitting sync functions onto a shut-down executor and leaking tasks after teardown.","triggerScenarios":"Calling any runtime entry point — ready, preflight, optimize, retrieve, observe, delete_session — after the runtime was used as an async context manager and exited, or after an explicit await aclose().","commonSituations":"Calling runtime methods outside the `async with` block; keeping a reference to the runtime after shutdown; a lifespan/handler that closes the runtime while other code still uses it.","solutions":["Keep all runtime calls inside the `async with runtime:` block","Re-create a new runtime instance if you need to use it after close","Track lifecycle in app code: don't close in one component while another still holds the runtime","Catch MiddlewareError with code \"closed\" and treat it as a lifecycle bug, not retryable"],"exampleFix":"// before\nruntime = AsyncRuntime(...)\nawait runtime.aclose()\nawait runtime.optimize(...)  # MiddlewareError: closed\n// after\nasync with AsyncRuntime(...) as runtime:\n    await runtime.optimize(...)  # all usage inside the context","handlingStrategy":"try-catch","validationCode":"if runtime_is_closed:\n    raise RuntimeError('runtime already closed; create a new instance')","typeGuard":"def runtime_open(runtime) -> bool:\n    return not getattr(runtime, '_closed', True)","tryCatchPattern":"try:\n    await runtime.optimize(msg)\nexcept MiddlewareError as e:\n    if str(e) == 'closed':\n        runtime = AsyncRuntime(...)  # recreate\n        await runtime.optimize(msg)\n    else:\n        raise","preventionTips":["Scope all runtime usage to `async with runtime:` blocks","Never store long-lived references to the runtime outside the owning component","Shut down the runtime only after all producers/consumers are joined","Expose runtime readiness through your app's health state, not raw attribute access"],"tags":["python","async","lifecycle","closed-resource"],"backgroundTag":"operation-on-closed-resource","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"}