{"record":{"id":"3aac554e2d9f1078","repo":"apache/beam","slug":"str-e-wrapped-s3-client-error-message","errorCode":null,"errorMessage":"str(e) (wrapped S3 client error message)","messagePattern":"str\\(e\\) \\(wrapped S3 client error message\\)","errorType":"exception","errorClass":"S3ClientError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/aws/clients/s3/boto3_client.py","lineNumber":95,"sourceCode":"    self._download_request = None\n    self._download_stream = None\n    self._download_pos = 0\n\n  def get_object_metadata(self, request):\n    \"\"\"Retrieves an object's metadata.\n\n    Args:\n      request: (GetRequest) input message\n\n    Returns:\n      (Object) The response message.\n    \"\"\"\n    kwargs = {'Bucket': request.bucket, 'Key': request.object}\n\n    try:\n      boto_response = self.client.head_object(**kwargs)\n    except Exception as e:\n      raise messages.S3ClientError(str(e), get_http_error_code(e))\n\n    item = messages.Item(\n        boto_response['ETag'],\n        request.object,\n        boto_response['LastModified'],\n        boto_response['ContentLength'],\n        boto_response['ContentType'])\n\n    return item\n\n  def get_stream(self, request, start):\n    \"\"\"Opens a stream object starting at the given position.\n\n    Args:\n      request: (GetRequest) request\n      start: (int) start offset\n    Returns:\n      (Stream) Boto3 stream object.","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/aws/clients/s3/boto3_client.py#L77-L113","documentation":"`Client.get_object_metadata` wraps any exception raised by boto3's `head_object` call into a `messages.S3ClientError`, attaching the AWS HTTP status code extracted from the exception's `response` attribute. The message is simply `str(e)` of the underlying boto3 `ClientError` (or other exception), so the actual cause (404 NoSuchKey, 403 AccessDenied, connection failure, etc.) is in the wrapped text. This is a uniform error-translator so the rest of the S3 filesystem layer sees one exception type with an HTTP code.","triggerScenarios":"Calling `get_object_metadata(request)` when the S3 HEAD request fails: the object key does not exist (404), the caller lacks s3:GetObject permission (403), the bucket does not exist or is in the wrong region, credentials are invalid/expired, or a network/timeout error occurs during `client.head_object`.","commonSituations":"File matching against a stale path after upstream step renamed keys; typos in bucket or prefix concatenation; IAM role missing GetObject; temporary AWS outage or throttling; boto3 not configured with region so endpoint resolution fails.","solutions":["Read the wrapped `str(e)` message: it contains the boto3 ClientError code (e.g. 'NoSuchKey', 'AccessDenied') and fix the underlying cause accordingly.","Verify the object key and bucket in the request are correct (print request.bucket / request.object); a 404 usually means the path or concatenation logic is wrong.","Check AWS credentials and IAM permissions (s3:GetObject on the key) via `aws s3api head-object` with the same credentials.","Ensure boto3 has a region configured (AWS_REGION env var, ~/.aws/config, or pipeline options) to avoid endpoint errors.","For transient errors, rely on the retry wrapper or catch S3ClientError and re-issue with backoff."],"exampleFix":"// before\nmetadata = client.get_object_metadata(messages.GetRequest(bucket='my-bucket', object='data/out.json'))\n// after\ntry:\n  metadata = client.get_object_metadata(messages.GetRequest(bucket='my-bucket', object='data/out.json'))\nexcept messages.S3ClientError as e:\n  if e.code == 404:\n    metadata = None  # treat missing object as empty\n  else:\n    raise","handlingStrategy":"try-catch","validationCode":"import boto3\ndef object_exists(bucket, key, s3=None):\n  s3 = s3 or boto3.client('s3')\n  try:\n    s3.head_object(Bucket=bucket, Key=key)\n    return True\n  except s3.exceptions.ClientError as e:\n    if e.response['Error']['Code'] in ('404', 'NoSuchKey', 'NotFound'):\n      return False\n    raise","typeGuard":"def is_not_found(err):\n  return isinstance(err, messages.S3ClientError) and getattr(err, 'code', None) == 404","tryCatchPattern":"try:\n  item = client.get_object_metadata(request)\nexcept messages.S3ClientError as e:\n  if getattr(e, 'code', None) == 404:\n    item = None\n  else:\n    raise","preventionTips":["Pre-check object existence with head_object when a miss is a normal case","Validate bucket and key construction (watch for double slashes, missing prefixes)","Ensure region and credentials are configured before pipeline start","Grant s3:GetObject to the executing IAM role","Wrap reads in retry with backoff for transient 5xx"],"tags":["aws","s3","boto3","network","http"],"backgroundTag":"api-error-response","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}