apache/druid · error · IOException (IOE)

Could not fetch last modified timestamp from URI [%s]

Error message

Could not fetch last modified timestamp from URI [%s]

What it means

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.

Source

Thrown at extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3DataSegmentPuller.java:311

   *
   * @param uri The URI to check the last timestamp
   * @return The time in ms of the last modification of the URI in String format
   * @throws IOException
   */
  @Override
  public String getVersion(URI uri) throws IOException
  {
    try {
      final CloudObjectLocation coords = new CloudObjectLocation(S3Utils.checkURI(uri));
      final HeadObjectResponse objectMetadata =
          S3Utils.getSingleObjectMetadata(s3Client, coords.getBucket(), coords.getPath());
      Instant lastModified = objectMetadata.lastModified();
      return StringUtils.format("%d", lastModified != null ? lastModified.toEpochMilli() : 0L);
    }
    catch (SdkException e) {
      if (AWSClientUtil.isClientExceptionRecoverable(e)) {
        // The recoverable logic is always true for IOException, so we want to only pass IOException if it is recoverable
        throw new IOE(e, "Could not fetch last modified timestamp from URI [%s]", uri);
      } else {
        throw new RE(e, "Error fetching last modified timestamp from URI [%s]", uri);
      }
    }
  }

}

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Retry the operation; the error is explicitly marked recoverable.
  2. Verify S3 connectivity/endpoint configuration (region, proxy, VPC endpoints).
  3. Check IAM credentials validity and that the bucket/key exist.
  4. Inspect the wrapped SdkException cause for the root AWS error code.

Example fix

// before
String v = puller.getVersion(uri);
// after
String v = RetryUtils.retry(() -> puller.getVersion(uri), s -> s instanceof IOException);
Defensive patterns

Strategy: retry

Validate before calling

if (!S3Utils.isObjectInBucket(s3Client, bucket, key)) throw new IllegalStateException("missing object");

Try / catch

try { v = puller.getVersion(uri); } catch (IOException e) { /* recoverable: retry with backoff */ v = RetryUtils.retry(() -> puller.getVersion(uri), s -> true); }

Prevention

When it happens

Trigger: Calling version()/getVersion() on a segment URI when the S3 HeadObject call throws a recoverable SdkException (transient network failure, throttling, 5xx, connection reset).

Common situations: 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.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/9105fec199b7dc94. Report an issue: GitHub.