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
- Log/inspect the exact URI at the call site to spot the empty component
- Set and validate required config (bucket name, key prefix) before building URIs
- Prefer write_stream(key) which builds the URI for you and returns a correct one
- 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
- Fail fast on empty config values (bucket env vars) before building URIs
- Validate URIs with a parse helper before storage/DB round-trips
- Prefer write_stream(key) which constructs well-formed URIs for you
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Bucket {bucket!r} in URI {uri!r} does not match configured b
- Invalid blob key: {key!r}
- S3BlobStore requires the optional dependency ``aioboto3``. I
- S3BlobStore is not entered; use it inside ``async with`` bef
- Not an S3 blob URI: {uri!r}
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/78dde92c1c4bb04d.
Report an issue: GitHub.