apache/druid · error · IllegalStateException (ISE)

S3StorageConfig cannot be null!

Error message

S3StorageConfig cannot be null!

What it means

ServerSideEncryptingAmazonS3.Builder.build() validates that both an S3 client supplier and an S3StorageConfig (encryption config) were provided before constructing the client. The S3StorageConfig carries server-side encryption settings (SSE-C keys, KMS/AES256 config) needed to wrap every S3 request. A null config means the builder was never given the encryption configuration, so requests would be sent without the required encryption material.

Source

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

     * Builds a new {@link ServerSideEncryptingAmazonS3} instance.
     *
     * <p><b>Resource leak warning:</b> Each instance created by this method holds internal resources such as thread
     * pools and connection pools. {@link ServerSideEncryptingAmazonS3} is not {@link java.io.Closeable}, so there is
     * currently no way for callers to release these resources when the instance is no longer needed. Avoid calling
     * this method repeatedly (e.g., once per file or per task) when a single shared instance would suffice. Consider
     * memoizing the result, as {@link org.apache.druid.data.input.s3.S3InputSource} does, to ensure the client is
     * created at most once per configuration.
     *
     * <p>The long-term fix is to make {@link ServerSideEncryptingAmazonS3} implement {@link java.io.Closeable} and
     * arrange for {@code close()} to be called appropriately, but that is a larger change deferred for the future.
     */
    public ServerSideEncryptingAmazonS3 build()
    {
      if (s3ClientSupplier == null) {
        throw new ISE("S3Client supplier cannot be null!");
      }
      if (s3StorageConfig == null) {
        throw new ISE("S3StorageConfig cannot be null!");
      }

      S3Client s3Client;
      try {
        s3Client = S3Utils.retryS3Operation(s3ClientSupplier::get);
      }
      catch (Exception e) {
        throw new RuntimeException(e);
      }

      S3AsyncClient s3AsyncClient = null;
      if (s3AsyncClientSupplier != null) {
        try {
          s3AsyncClient = S3Utils.retryS3Operation(s3AsyncClientSupplier::get);
        }
        catch (Exception e) {
          log.warn(e, "Failed to create S3AsyncClient, falling back to sync uploads");
        }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set the server-side encryption properties (e.g. druid.storage.s3.serverSideEncryptionType and related key properties) so the config is populated.
  2. When building programmatically, call builder().withS3StorageConfig(new ServerSideEncryptingAmazonS3.NoopServerSideEncrypting()) if no encryption is desired.
  3. Verify the extension wiring/config bean for S3StorageConfig is injected into the code path constructing the builder.
  4. Add an early validation in your module setup that fails with a clearer message if encryption config is absent.

Example fix

// before
ServerSideEncryptingAmazonS3 s3 = ServerSideEncryptingAmazonS3.builder()
    .setS3ClientSupplier(supplier)
    .build(); // throws: S3StorageConfig cannot be null!
// after
ServerSideEncryptingAmazonS3 s3 = ServerSideEncryptingAmazonS3.builder()
    .setS3ClientSupplier(supplier)
    .withS3StorageConfig(new ServerSideEncryptingAmazonS3.NoopServerSideEncrypting())
    .build();
Defensive patterns

Strategy: validation

Validate before calling

if (config == null) {
  throw new IllegalArgumentException("s3StorageConfig must be set before build(); check serverSideEncryption config properties");
}
ServerSideEncryptingAmazonS3 s3 = ServerSideEncryptingAmazonS3.builder()
    .setS3ClientSupplier(supplier)
    .withS3StorageConfig(config)
    .build();

Type guard

boolean hasStorageConfig(ServerSideEncryptingAmazonS3.Builder b) { return b != null; } // builder API: ensure withS3StorageConfig called; verify via config bean != null

Try / catch

try { s3 = builder.build(); } catch (IllegalStateException e) { if (e.getMessage().contains("S3StorageConfig")) { /* supply config and retry */ } else throw e; }

Prevention

When it happens

Trigger: Calling ServerSideEncryptingAmazonS3.builder().build() without invoking .withS3StorageConfig(...), or programmatically constructing the S3 client in an extension/task where the druid.storage.s3 serverSideEncryption config properties were absent so the config bean was never populated.

Common situations: Custom ingestion tasks or test harnesses building the S3 client manually; Druid configs missing serverSideEncryption.* properties in runtime.properties; upgrades where a module that previously injected a default (NoopServerSideEncryptingAmazonS3) config no longer does.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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