{"record":{"id":"43dc0f698ae961c1","repo":"apache/beam","slug":"str-e","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"exception","errorClass":"S3ClientError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/aws/clients/s3/boto3_client.py","lineNumber":133,"sourceCode":"\n    if self._download_request and (\n        start != self._download_pos or\n        request.bucket != self._download_request.bucket or\n        request.object != self._download_request.object):\n      self._download_stream.close()\n      self._download_stream = None\n\n    # noinspection PyProtectedMember\n    if not self._download_stream or self._download_stream._raw_stream.closed:\n      try:\n        self._download_stream = self.client.get_object(\n            Bucket=request.bucket,\n            Key=request.object,\n            Range='bytes={}-'.format(start))['Body']\n        self._download_request = request\n        self._download_pos = start\n      except Exception as e:\n        raise messages.S3ClientError(str(e), get_http_error_code(e))\n\n    return self._download_stream\n\n  @retry.with_exponential_backoff()\n  def get_range(self, request, start, end):\n    r\"\"\"Retrieves an object's contents.\n\n      Args:\n        request: (GetRequest) request\n        start: (int) start offset\n        end: (int) end offset (exclusive)\n      Returns:\n        (bytes) The response message.\n      \"\"\"\n    for i in range(2):\n      try:\n        stream = self.get_stream(request, start)\n        data = stream.read(end - start)","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/aws/clients/s3/boto3_client.py#L115-L151","documentation":"`Client.get_stream` opens a ranged download (`get_object` with a `bytes=N-` Range header) and wraps any failure of that boto3 call in a `messages.S3ClientError` carrying the AWS HTTP status code. The message is the raw `str(e)` from boto3, so the real cause (404, 403, throttling, connection reset) is embedded in the wrapped exception text. The stream is later consumed by `get_range`, which has its own recovery path.","triggerScenarios":"Calling `get_stream(request, start)` where the initial ranged GET fails: key missing (404), no s3:GetObject permission (403), invalid/expired credentials, S3 throttling (503 SlowDown), or network interruption while opening the download body.","commonSituations":"Reading large files that were deleted or overwritten mid-pipeline; IAM roles without read access to a bucket; long-running jobs whose temporary credentials expired; S3 throttling under heavy parallel reads.","solutions":["Inspect the wrapped message for the boto3 error code (NoSuchKey, AccessDenied, SlowDown) and fix that root cause.","Confirm the object exists and the key/bucket in the request are correct before reading.","Check IAM permissions and credential freshness (s3:GetObject on bucket/key).","For 503/timeout errors, retry: `get_range` already retries with exponential backoff; add your own backoff for stream creation.","Reduce parallelism or request rate if seeing SlowDown/throttling."],"exampleFix":"// before\nstream = client.get_stream(request, start=0)\n// after\ntry:\n  stream = client.get_stream(request, start=0)\nexcept messages.S3ClientError as e:\n  if e.code in (500, 503):\n    time.sleep(2); stream = client.get_stream(request, start=0)\n  else:\n    raise","handlingStrategy":"retry","validationCode":"import boto3\ndef can_read(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:\n    return False","typeGuard":"def is_retryable(err):\n  code = getattr(err, 'code', None)\n  return isinstance(err, messages.S3ClientError) and code in (500, 502, 503, 504, None)","tryCatchPattern":"try:\n  stream = client.get_stream(request, start)\nexcept messages.S3ClientError as e:\n  if getattr(e, 'code', None) in (500, 503):\n    stream = retry.with_exponential_backoff()(client.get_stream)(request, start)\n  else:\n    raise","preventionTips":["Verify the object exists before opening a stream","Refresh temporary credentials before long read sessions","Back off on 503 SlowDown and reduce concurrent readers","Keep boto3/botocore updated for connection-handling fixes","Check proxy idle-timeout settings for long downloads"],"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"}