agentscope-ai/agentscope · error · RuntimeError

S3BlobStore is not entered; use it inside ``async with`` bef

Error message

S3BlobStore is not entered; use it inside ``async with`` before calling blob methods.

What it means

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'.

Source

Thrown at src/agentscope/app/rag/blob_store/_s3.py:160

                "Install it with ``uv pip install aioboto3`` or with the "
                "``[s3]`` extra.",
            ) from e

        self._session = aioboto3.Session()
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: Any,
    ) -> None:
        self._session = None

    def _client(self) -> Any:
        """Open a fresh S3 client context manager for one call."""
        if self._session is None:
            raise RuntimeError(
                "S3BlobStore is not entered; use it inside "
                "``async with`` before calling blob methods.",
            )
        return self._session.client(
            "s3",
            region_name=self._region_name,
            endpoint_url=self._endpoint_url,
            aws_access_key_id=self._aws_access_key_id,
            aws_secret_access_key=self._aws_secret_access_key,
            aws_session_token=self._session_token,
            use_ssl=self._use_ssl,
            config=self._config,
        )

    @staticmethod
    def _parse_uri(uri: str) -> tuple[str, str]:
        """Split an ``s3://{bucket}/{key}`` URI into ``(bucket, key)``."""
        if not uri.startswith(_SCHEME):

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Wrap usage: 'async with S3BlobStore(...) as store: await store.write_stream(...)'
  2. 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
  3. Re-enter the store after it has been closed instead of reusing it
  4. Check for this RuntimeError in integration smoke tests to catch lifecycle bugs early

Example fix

# before
store = S3BlobStore(bucket='b')
await store.write_stream('k', stream)  # RuntimeError: not entered

# after
async with S3BlobStore(bucket='b') as store:
    await store.write_stream('k', stream)
Defensive patterns

Strategy: try-catch

Validate before calling

# Enter once per task via AsyncExitStack for long-lived stores
from contextlib import AsyncExitStack
stack = AsyncExitStack()
store = await stack.enter_async_context(S3BlobStore(bucket=b))
# keep `stack` open for the app lifetime; close on shutdown

Type guard

null

Try / catch

try:
    await store.write_stream(key, stream)
except RuntimeError as e:
    if 'not entered' in str(e):
        async with store:
            await store.write_stream(key, stream)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/407b5719abf1688d. Report an issue: GitHub.