agentscope-ai/agentscope · error · ValueError

Bucket {bucket!r} in URI {uri!r} does not match configured b

Error message

Bucket {bucket!r} in URI {uri!r} does not match configured bucket {expected_bucket!r}.

What it means

For mutating operations (delete, exists), S3BlobStore._key_from_uri asserts the bucket in the s3:// URI equals the store's configured bucket, since the store only owns objects in its own bucket. A mismatch raises this ValueError, preventing accidental cross-bucket deletes. (open/write paths parse the URI but this strict check applies to the mutating path.)

Source

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

        if not uri.startswith(_SCHEME):
            raise ValueError(f"Not an S3 blob URI: {uri!r}")
        rest = uri[len(_SCHEME) :]
        bucket, _, key = rest.partition("/")
        if not bucket or not key:
            raise ValueError(f"Malformed S3 blob URI: {uri!r}")
        return bucket, key

    @classmethod
    def _key_from_uri(cls, uri: str, expected_bucket: str) -> str:
        """Return the object key, asserting the bucket matches.

        Used by mutating operations (``delete``, ``exists``) where
        crossing into another bucket would be a bug — the configured
        bucket is the only place the store owns objects.
        """
        bucket, key = cls._parse_uri(uri)
        if bucket != expected_bucket:
            raise ValueError(
                f"Bucket {bucket!r} in URI {uri!r} does not match "
                f"configured bucket {expected_bucket!r}.",
            )
        return key

    async def write_stream(self, key: str, stream: IO[bytes]) -> str:
        """Stream-write a blob and return its ``s3://{bucket}/{key}`` URI.

        Uses ``upload_fileobj`` so aioboto3 picks multipart upload
        automatically for bodies above the multipart threshold
        (8 MiB by default in botocore). For smaller bodies it
        promotes to a single ``PutObject`` call.
        """
        async with self._client() as s3:
            await s3.upload_fileobj(stream, self._bucket, key)
        return f"{_SCHEME}{self._bucket}/{key}"

    @asynccontextmanager

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Instantiate a store for the URI's bucket: async with S3BlobStore(bucket=uri_bucket) as s: await s.delete(uri)
  2. Verify the bucket name in config matches where the blob was written
  3. When renaming/migrating buckets, rewrite stored URIs to the new bucket
  4. Add a pre-check helper: parse the URI and compare buckets before calling delete

Example fix

# before
store = S3BlobStore(bucket='prod-blobs')
async with store as s:
    await s.delete('s3://staging-blobs/x')  # ValueError: bucket mismatch

# after
from urllib.parse import urlparse
bucket = urlparse(uri).netloc
async with S3BlobStore(bucket=bucket) as s:
    await s.delete(uri)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def bucket_of(uri: str) -> str:
    return urlparse(uri).netloc

if bucket_of(uri) != configured_bucket:
    raise ValueError(f'URI bucket {bucket_of(uri)!r} != store bucket {configured_bucket!r}')

Type guard

def uri_bucket_matches(uri: str, store_bucket: str) -> bool:
    return uri.startswith('s3://') and urlparse(uri).netloc == store_bucket

Try / catch

try:
    await store.delete(uri)
except ValueError as e:
    if 'does not match configured bucket' in str(e):
        ...  # open a store for the URI's bucket and retry
    raise

Prevention

When it happens

Trigger: Calling s3store.delete('s3://other-bucket/x') or s3store.exists(...) where the URI references a bucket different from the one passed to S3BlobStore(bucket=...).

Common situations: Multiple buckets (staging vs prod, per-tenant buckets) and URIs from one bucket passed to a store configured for another; copied URIs between environments; bucket renamed in config but old URIs still stored in the DB.

Related errors


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