{"record":{"id":"9105fec199b7dc94","repo":"apache/druid","slug":"could-not-fetch-last-modified-timestamp-from-uri","errorCode":null,"errorMessage":"Could not fetch last modified timestamp from URI [%s]","messagePattern":"Could not fetch last modified timestamp from URI \\[(.+?)\\]","errorType":"exception","errorClass":"IOException (IOE)","httpStatus":null,"severity":"error","filePath":"extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3DataSegmentPuller.java","lineNumber":311,"sourceCode":"   *\n   * @param uri The URI to check the last timestamp\n   * @return The time in ms of the last modification of the URI in String format\n   * @throws IOException\n   */\n  @Override\n  public String getVersion(URI uri) throws IOException\n  {\n    try {\n      final CloudObjectLocation coords = new CloudObjectLocation(S3Utils.checkURI(uri));\n      final HeadObjectResponse objectMetadata =\n          S3Utils.getSingleObjectMetadata(s3Client, coords.getBucket(), coords.getPath());\n      Instant lastModified = objectMetadata.lastModified();\n      return StringUtils.format(\"%d\", lastModified != null ? lastModified.toEpochMilli() : 0L);\n    }\n    catch (SdkException e) {\n      if (AWSClientUtil.isClientExceptionRecoverable(e)) {\n        // The recoverable logic is always true for IOException, so we want to only pass IOException if it is recoverable\n        throw new IOE(e, \"Could not fetch last modified timestamp from URI [%s]\", uri);\n      } else {\n        throw new RE(e, \"Error fetching last modified timestamp from URI [%s]\", uri);\n      }\n    }\n  }\n\n}\n","sourceCodeStart":293,"sourceCodeEnd":319,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3DataSegmentPuller.java#L293-L319","documentation":"S3DataSegmentPuller.getVersion fetches an S3 object's metadata to read its last-modified timestamp as the segment version string. When the S3 SDK raises an SdkException classified as recoverable, it is wrapped in an IOException with this message. Recoverable means the caller may succeed on retry.","triggerScenarios":"Calling version()/getVersion() on a segment URI when the S3 HeadObject call throws a recoverable SdkException (transient network failure, throttling, 5xx, connection reset).","commonSituations":"Transient S3 outages or throttling during historical segment loading; misconfigured proxy or DNS causing intermittent connectivity failures; expired credentials sometimes surfacing as recoverable SDK errors.","solutions":["Retry the operation; the error is explicitly marked recoverable.","Verify S3 connectivity/endpoint configuration (region, proxy, VPC endpoints).","Check IAM credentials validity and that the bucket/key exist.","Inspect the wrapped SdkException cause for the root AWS error code."],"exampleFix":"// before\nString v = puller.getVersion(uri);\n// after\nString v = RetryUtils.retry(() -> puller.getVersion(uri), s -> s instanceof IOException);","handlingStrategy":"retry","validationCode":"if (!S3Utils.isObjectInBucket(s3Client, bucket, key)) throw new IllegalStateException(\"missing object\");","typeGuard":null,"tryCatchPattern":"try { v = puller.getVersion(uri); } catch (IOException e) { /* recoverable: retry with backoff */ v = RetryUtils.retry(() -> puller.getVersion(uri), s -> true); }","preventionTips":["Retry recoverable IOEs with backoff","Monitor S3 error rates and configure retries on the S3 client","Validate segment URIs before loading"],"tags":["aws","s3","io","retryable","network"],"backgroundTag":"http-request-failed","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}