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
- Set the server-side encryption properties (e.g. druid.storage.s3.serverSideEncryptionType and related key properties) so the config is populated.
- When building programmatically, call builder().withS3StorageConfig(new ServerSideEncryptingAmazonS3.NoopServerSideEncrypting()) if no encryption is desired.
- Verify the extension wiring/config bean for S3StorageConfig is injected into the code path constructing the builder.
- 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
- Always set serverSideEncryption.* properties when S3 deep storage is used.
- Use Druid's injected S3StorageConfig bean rather than constructing the builder by hand.
- Add a unit test asserting client construction with your module's config.
- Default to NoopServerSideEncryptingAmazonS3 explicitly when encryption is not required.
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
- Failed to apply webClientOptions to WebClientOptions. Check
- Failed to merge existing aggregators when generating metrics
- No segments found for compaction. Please check that datasour
- DynamicPartitionsSpec must be used for best-effort rollup
- Unable to create RecordSupplier: %s
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/94574c5ca95c131f.
Report an issue: GitHub.