{"record":{"id":"6745dc296dcc1537","repo":"cocoindex-io/cocoindex","slug":"invalid-s3-uri-uri-r-expected-s3-bucket-key","errorCode":null,"errorMessage":"Invalid S3 URI {uri!r}: expected 's3://bucket/key'.","messagePattern":"Invalid S3 URI (.+?): expected 's3://bucket/key'\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/cocoindex/connectors/amazon_s3/_source.py","lineNumber":50,"sourceCode":"from cocoindex.resources import file\n\n\ndef _etag_to_fingerprint(etag: object) -> bytes | None:\n    \"\"\"Convert an S3 ETag (a ``str``) to the ``bytes`` content fingerprint.\n\n    botocore returns ETags as quoted strings (e.g. ``'\"d41d8cd9...\"'``), but\n    :class:`~cocoindex.resources.file.FileMetadata.content_fingerprint` is typed\n    ``bytes``.  Storing the raw ``str`` makes the memo state's\n    ``tuple[datetime, bytes]`` round-trip fail on re-run (msgspec encodes a str\n    but the decoder expects bin), so encode it to bytes here.\n    \"\"\"\n    return etag.encode(\"utf-8\") if isinstance(etag, str) else None\n\n\ndef _parse_s3_uri(uri: str) -> tuple[str, str]:\n    \"\"\"Parse an ``s3://bucket/key`` URI into *(bucket_name, key)*.\"\"\"\n    if not uri.startswith(\"s3://\"):\n        raise ValueError(f\"Invalid S3 URI {uri!r}: expected 's3://bucket/key'.\")\n    without_scheme = uri[len(\"s3://\") :]\n    slash_idx = without_scheme.find(\"/\")\n    if slash_idx == -1:\n        raise ValueError(f\"Invalid S3 URI {uri!r}: expected 's3://bucket/key'.\")\n    return without_scheme[:slash_idx], without_scheme[slash_idx + 1 :]\n\n\nclass S3FilePath(file.FilePath[str]):\n    \"\"\"\n    File path for Amazon S3 objects.\n\n    The resolved path is the full S3 object key (string).\n    The relative path is the object key relative to the walker prefix (or the full key\n    if no prefix was used).\n    \"\"\"\n\n    __slots__ = (\"_bucket_name\", \"_object_key\")\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/cocoindex-io/cocoindex/blob/e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b/python/cocoindex/connectors/amazon_s3/_source.py#L32-L68","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nf = await amazon_s3.get_object(client, \"my-bucket/data/config.json\")\n\n// after\nf = await amazon_s3.get_object(client, \"s3://my-bucket/data/config.json\")","handlingStrategy":"validation","validationCode":"def ensure_s3_uri(uri: str) -> str:\n    if not uri.startswith(\"s3://\"):\n        raise ValueError(f\"not an s3 URI: {uri!r}\")\n    if \"/\" not in uri[5:]:\n        raise ValueError(f\"s3 URI missing key: {uri!r}\")\n    return uri","typeGuard":"def is_s3_uri(value: object) -> bool:\n    return isinstance(value, str) and value.startswith(\"s3://\") and \"/\" in value[5:]","tryCatchPattern":"try:\n    f = await amazon_s3.get_object(client, uri)\nexcept ValueError as e:\n    logger.error(\"bad S3 URI: %s\", e)\n    raise","preventionTips":["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."],"tags":["python","s3","uri","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"e84aa99b3292c5270a4b313b2a7137ad9ce8ab3b","analyzedAt":"2026-09-08T15:59:19.997Z","contentChangedAt":"2026-09-08T15:59:19.997Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}