agentscope-ai/agentscope · error · ValueError

Malformed S3 blob URI: {uri!r}

Error message

Malformed S3 blob URI: {uri!r}

What it means

After the s3:// scheme check, _parse_uri requires both a non-empty bucket and a non-empty key: 's3://' alone, 's3://bucket' (no key), or 's3:///key' (no bucket) raise ValueError('Malformed S3 blob URI'). This catches truncated or template-interpolated URIs where a component came out empty.

Source

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

            "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):
            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

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Log/inspect the exact URI at the call site to spot the empty component
  2. Set and validate required config (bucket name, key prefix) before building URIs
  3. Prefer write_stream(key) which builds the URI for you and returns a correct one
  4. Add a startup assertion: assert bucket and key when constructing URIs manually

Example fix

# before
uri = f"s3://{os.environ.get('BUCKET')}/{key}"  # BUCKET unset -> malformed
await s3store.open(uri)

# after
bucket = os.environ['BUCKET']  # fails fast if missing
await s3store.open(f"s3://{bucket}/{key}")
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def valid_s3_uri(uri: str) -> bool:
    if not uri.startswith('s3://'):
        return False
    parts = uri[5:].split('/', 1)
    return len(parts) == 2 and all(parts)

assert valid_s3_uri(uri), f'malformed S3 URI: {uri!r}'

Type guard

from urllib.parse import urlparse

def parse_s3_uri(uri: str) -> tuple[str, str] | None:
    if not uri.startswith('s3://'):
        return None
    bucket, _, key = uri[5:].partition('/')
    return (bucket, key) if bucket and key else None

Try / catch

try:
    await store.open(uri)
except ValueError as e:
    if 'Malformed S3 blob URI' in str(e):
        ...  # log uri, fix bucket/key construction
    raise

Prevention

When it happens

Trigger: Passing 's3://my-bucket' (missing /key), 's3:///k' (empty bucket), or URIs built with an unset env/config variable, e.g. f"s3://{bucket}/{key}" with bucket=None rendering as 's3://None/k' variants or empty strings.

Common situations: Config-driven URI construction where BUCKET_NAME env var is unset in one environment; templating bugs dropping the key; string-concatenated URIs missing a slash producing 's3://bucketkey'.

Understand the failure class

Related errors


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