elastic/elasticsearch · error · SslConfigException

cannot configure SSL/TLS without any supported cipher suites

Error message

cannot configure SSL/TLS without any supported cipher suites

What it means

The SslConfiguration constructor rejects a null or empty cipher list because an empty cipher set would make TLS negotiation impossible. This is a hard precondition on the value passed as `ciphers`.

Source

Thrown at libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfiguration.java:81

        protocolAlgorithmMap.put("SSLv2Hello", "SSL");
        ORDERED_PROTOCOL_ALGORITHM_MAP = Collections.unmodifiableMap(protocolAlgorithmMap);
    }

    public SslConfiguration(
        String settingPrefix,
        boolean explicitlyConfigured,
        SslTrustConfig trustConfig,
        SslKeyConfig keyConfig,
        SslVerificationMode verificationMode,
        SslClientAuthenticationMode clientAuth,
        List<String> ciphers,
        List<String> supportedProtocols,
        long handshakeTimeoutMillis
    ) {
        this.settingPrefix = settingPrefix;
        this.explicitlyConfigured = explicitlyConfigured;
        if (ciphers == null || ciphers.isEmpty()) {
            throw new SslConfigException("cannot configure SSL/TLS without any supported cipher suites");
        }
        if (supportedProtocols == null || supportedProtocols.isEmpty()) {
            throw new SslConfigException("cannot configure SSL/TLS without any supported protocols");
        }
        this.trustConfig = Objects.requireNonNull(trustConfig, "trust config cannot be null");
        this.keyConfig = Objects.requireNonNull(keyConfig, "key config cannot be null");
        this.verificationMode = Objects.requireNonNull(verificationMode, "verification mode cannot be null");
        this.clientAuth = Objects.requireNonNull(clientAuth, "client authentication cannot be null");
        if (handshakeTimeoutMillis < 1L) {
            throw new SslConfigException("handshake timeout must be at least 1ms");
        }
        this.handshakeTimeoutMillis = handshakeTimeoutMillis;
        this.ciphers = Collections.unmodifiableList(ciphers);
        this.supportedProtocols = Collections.unmodifiableList(supportedProtocols);
    }

    public List<String> getCipherSuites() {
        return ciphers;

View on GitHub (pinned to db6a809a66)

Solutions

  1. Pass a non-empty cipher list, ideally from SslConfigurationLoader.DEFAULT_CIPHERS.
  2. If loading from settings, remove the empty ssl.cipher_suites override so defaults apply.
  3. In custom loaders, never return an empty list from the cipher resolution path — fall back to DEFAULT_CIPHERS.

Example fix

// before
new SslConfiguration(prefix, true, trust, key, mode, auth,
    List.of(), protocols, 10_000L); // empty ciphers
// after
new SslConfiguration(prefix, true, trust, key, mode, auth,
    SslConfigurationLoader.DEFAULT_CIPHERS, protocols, 10_000L);
Defensive patterns

Strategy: validation

Validate before calling

List<String> ensureCiphers(List<String> ciphers) {
    if (ciphers == null || ciphers.isEmpty())
        return SslConfigurationLoader.DEFAULT_CIPHERS;
    return ciphers;
}

Prevention

When it happens

Trigger: Constructing SslConfiguration with ciphers=null or ciphers=[]. In normal use this is reached only if SslConfigurationLoader resolves ssl.cipher_suites to an empty list AND the default fallback is also empty — the loader has its own empty-check (error 830) that normally fires first.

Common situations: A custom SslConfigurationLoader subclass overrides default ciphers to empty; programmatic construction of SslConfiguration with an explicitly empty list; a test helper that builds a config without populating ciphers.

Understand the failure class

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/4865cfe7c3f19e80. Report an issue: GitHub.