cocoindex-io/cocoindex · error · ValueError
Invalid S3 URI {uri!r}: expected 's3://bucket/key'.
Error message
Invalid S3 URI {uri!r}: expected 's3://bucket/key'. What it means
_parse_s3_uri validates that an S3 URI starts with the `s3://` scheme before splitting it into bucket and key. When the string does not begin with `s3://`, it refuses to parse and raises this ValueError instead of producing a wrong bucket/key pair.
Source
Thrown at python/cocoindex/connectors/amazon_s3/_source.py:50
from cocoindex.resources import file
def _etag_to_fingerprint(etag: object) -> bytes | None:
"""Convert an S3 ETag (a ``str``) to the ``bytes`` content fingerprint.
botocore returns ETags as quoted strings (e.g. ``'"d41d8cd9..."'``), but
:class:`~cocoindex.resources.file.FileMetadata.content_fingerprint` is typed
``bytes``. Storing the raw ``str`` makes the memo state's
``tuple[datetime, bytes]`` round-trip fail on re-run (msgspec encodes a str
but the decoder expects bin), so encode it to bytes here.
"""
return etag.encode("utf-8") if isinstance(etag, str) else None
def _parse_s3_uri(uri: str) -> tuple[str, str]:
"""Parse an ``s3://bucket/key`` URI into *(bucket_name, key)*."""
if not uri.startswith("s3://"):
raise ValueError(f"Invalid S3 URI {uri!r}: expected 's3://bucket/key'.")
without_scheme = uri[len("s3://") :]
slash_idx = without_scheme.find("/")
if slash_idx == -1:
raise ValueError(f"Invalid S3 URI {uri!r}: expected 's3://bucket/key'.")
return without_scheme[:slash_idx], without_scheme[slash_idx + 1 :]
class S3FilePath(file.FilePath[str]):
"""
File path for Amazon S3 objects.
The resolved path is the full S3 object key (string).
The relative path is the object key relative to the walker prefix (or the full key
if no prefix was used).
"""
__slots__ = ("_bucket_name", "_object_key")
View on GitHub (pinned to e84aa99b32)
Solutions
- Prefix the argument with `s3://`: use `s3://my-bucket/data/config.json`.
- If you only have bucket and key separately, call get_object(client, bucket, key) with two arguments instead.
- Normalize programmatic input: f"s3://{bucket}/{key}" before calling the API.
- Strip non-s3 schemes first if you receive https URLs and convert them to s3:// form.
Example fix
// before f = await amazon_s3.get_object(client, "my-bucket/data/config.json") // after f = await amazon_s3.get_object(client, "s3://my-bucket/data/config.json")
Defensive patterns
Strategy: validation
Validate before calling
def ensure_s3_uri(uri: str) -> str:
if not uri.startswith("s3://"):
raise ValueError(f"not an s3 URI: {uri!r}")
if "/" not in uri[5:]:
raise ValueError(f"s3 URI missing key: {uri!r}")
return uri Type guard
def is_s3_uri(value: object) -> bool:
return isinstance(value, str) and value.startswith("s3://") and "/" in value[5:] Try / catch
try:
f = await amazon_s3.get_object(client, uri)
except ValueError as e:
logger.error("bad S3 URI: %s", e)
raise Prevention
- Build URIs with f"s3://{bucket}/{key}" instead of concatenation.
- Use the two-argument get_object(client, bucket, key) form to avoid URI construction entirely.
- Validate URIs at config-load time.
When it happens
Trigger: Calling amazon_s3.get_object(client, 'my-bucket/data/config.json') or amazon_s3.read(client, 'my-bucket/key') with a URI missing the `s3://` prefix.
Common situations: Passing a bare bucket/key string, an https:// console URL, or a path copied from a local filesystem into functions documented to take an s3:// URI.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Cannot specify both an S3 URI and a separate key. Pass eithe
- key must be provided when bucket_name_or_uri is not an S3 UR
- expected None{loc}, got {type(value).__name__}
- expected {tp}{loc}, got {type(value).__name__}: {value!r}
- expected tuple{loc}, got {type(value).__name__}
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/6745dc296dcc1537.
Report an issue: GitHub.