{"record":{"id":"407b5719abf1688d","repo":"agentscope-ai/agentscope","slug":"s3blobstore-is-not-entered-use-it-inside-async","errorCode":null,"errorMessage":"S3BlobStore is not entered; use it inside ``async with`` before calling blob methods.","messagePattern":"S3BlobStore is not entered; use it inside ``async with`` before calling blob methods\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/agentscope/app/rag/blob_store/_s3.py","lineNumber":160,"sourceCode":"                \"Install it with ``uv pip install aioboto3`` or with the \"\n                \"``[s3]`` extra.\",\n            ) from e\n\n        self._session = aioboto3.Session()\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_val: BaseException | None,\n        exc_tb: Any,\n    ) -> None:\n        self._session = None\n\n    def _client(self) -> Any:\n        \"\"\"Open a fresh S3 client context manager for one call.\"\"\"\n        if self._session is None:\n            raise RuntimeError(\n                \"S3BlobStore is not entered; use it inside \"\n                \"``async with`` before calling blob methods.\",\n            )\n        return self._session.client(\n            \"s3\",\n            region_name=self._region_name,\n            endpoint_url=self._endpoint_url,\n            aws_access_key_id=self._aws_access_key_id,\n            aws_secret_access_key=self._aws_secret_access_key,\n            aws_session_token=self._session_token,\n            use_ssl=self._use_ssl,\n            config=self._config,\n        )\n\n    @staticmethod\n    def _parse_uri(uri: str) -> tuple[str, str]:\n        \"\"\"Split an ``s3://{bucket}/{key}`` URI into ``(bucket, key)``.\"\"\"\n        if not uri.startswith(_SCHEME):","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/app/rag/blob_store/_s3.py#L142-L178","documentation":"Every S3 operation goes through S3BlobStore._client(), which requires an active aioboto3 Session created in __aenter__. If self._session is None — i.e. the store was never entered (or already exited via __aexit__/close which nulls the session) — the method raises RuntimeError telling you to use the store inside 'async with'.","triggerScenarios":"Calling store.write_stream/open/delete/size/exists on an S3BlobStore that was constructed but never entered with 'async with', or after the context has exited (session torn down), or when reusing a store after an exception closed it.","commonSituations":"Creating the store at module scope but forgetting the context manager; keeping a long-lived store object across requests while the entering task already finished; calling blob methods in cleanup/shutdown code after __aexit__ ran.","solutions":["Wrap usage: 'async with S3BlobStore(...) as store: await store.write_stream(...)'","If you need a long-lived store, enter one session per request/task or use contextlib.AsyncExitStack to keep it open for the app lifetime","Re-enter the store after it has been closed instead of reusing it","Check for this RuntimeError in integration smoke tests to catch lifecycle bugs early"],"exampleFix":"# before\nstore = S3BlobStore(bucket='b')\nawait store.write_stream('k', stream)  # RuntimeError: not entered\n\n# after\nasync with S3BlobStore(bucket='b') as store:\n    await store.write_stream('k', stream)","handlingStrategy":"try-catch","validationCode":"# Enter once per task via AsyncExitStack for long-lived stores\nfrom contextlib import AsyncExitStack\nstack = AsyncExitStack()\nstore = await stack.enter_async_context(S3BlobStore(bucket=b))\n# keep `stack` open for the app lifetime; close on shutdown","typeGuard":"null","tryCatchPattern":"try:\n    await store.write_stream(key, stream)\nexcept RuntimeError as e:\n    if 'not entered' in str(e):\n        async with store:\n            await store.write_stream(key, stream)\n    else:\n        raise","preventionTips":["Always use 'async with S3BlobStore(...)' in the same scope as blob calls","Use contextlib.AsyncExitStack to manage the store's lifetime in services","Never call blob methods after __aexit__/close; re-enter instead"],"tags":["s3","blob-store","lifecycle","context-manager"],"backgroundTag":"resource-not-initialized","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}