{"record":{"id":"f6096f7d2a505101","repo":"apache/druid","slug":"error-fetching-last-modified-timestamp-from-uri","errorCode":null,"errorMessage":"Error fetching last modified timestamp from URI [%s]","messagePattern":"Error fetching last modified timestamp from URI \\[(.+?)\\]","errorType":"exception","errorClass":"RuntimeException (RE)","httpStatus":null,"severity":"error","filePath":"extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3DataSegmentPuller.java","lineNumber":313,"sourceCode":"   * @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":295,"sourceCodeEnd":319,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3DataSegmentPuller.java#L295-L319","documentation":"Same code path as the recoverable variant, but thrown when the SdkException from the HeadObject call is classified as non-recoverable. It is wrapped in a RuntimeException (RE) because retrying is not expected to help.","triggerScenarios":"Calling version()/getVersion() when S3 returns a permanent error: 404 NoSuchKey/403 AccessDenied, invalid credentials, malformed request, or other non-retryable SDK failures.","commonSituations":"Segment descriptor points to a deleted or never-uploaded object; wrong bucket or region configuration; IAM policy missing s3:GetObject on the segment prefix.","solutions":["Verify the segment URI (bucket/key) exists in S3.","Check IAM credentials and policy for s3:GetObject/s3:ListBucket permissions.","Confirm the configured region/endpoint matches the bucket's region.","Fix the underlying configuration; do not retry, the error is permanent."],"exampleFix":"// before\nString v = puller.getVersion(maybeMissingUri);\n// after\nif (S3Utils.isObjectInBucket(s3Client, bucket, key)) {\n  String v = puller.getVersion(maybeMissingUri);\n}","handlingStrategy":"try-catch","validationCode":"boolean exists = S3Utils.isObjectInBucket(s3Client, bucket, key);","typeGuard":null,"tryCatchPattern":"try { v = puller.getVersion(uri); } catch (RuntimeException e) { log.error(\"Permanent failure fetching %s\", uri, e); throw e; }","preventionTips":["Verify segment existence before metadata fetch","Audit IAM policies for s3:GetObject","Keep segment metadata and S3 objects in sync (don't delete during queries)"],"tags":["aws","s3","unrecoverable","permissions"],"backgroundTag":"resource-not-found","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"}