elastic/elasticsearch · error · SslConfigException

no protocols configured in [{}]

Error message

no protocols configured in [{}]

What it means

After resolving protocols from settings (with defaults), the loader checks that the resolved list is non-null and non-empty before constructing SslConfiguration. The error message echoes the full setting key (prefix + "supported_protocols") so the operator knows which key to fix.

Source

Thrown at libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationLoader.java:319

        final List<String> ciphers = resolveListSetting(CIPHERS, Function.identity(), defaultCiphers);
        final SslVerificationMode verificationMode = resolveSetting(VERIFICATION_MODE, SslVerificationMode::parse, defaultVerificationMode);
        final SslClientAuthenticationMode clientAuth = resolveSetting(CLIENT_AUTH, SslClientAuthenticationMode::parse, defaultClientAuth);
        final List<X509Field> trustRestrictionsX509Fields = resolveListSetting(
            TRUST_RESTRICTIONS_X509_FIELDS,
            X509Field::parseForRestrictedTrust,
            defaultRestrictedTrustFields
        );
        final long handshakeTimeoutMillis = resolveSetting(
            HANDSHAKE_TIMEOUT,
            s -> TimeValue.parseTimeValue(s, HANDSHAKE_TIMEOUT),
            DEFAULT_HANDSHAKE_TIMEOUT
        ).millis();

        final SslKeyConfig keyConfig = buildKeyConfig(basePath);
        final SslTrustConfig trustConfig = buildTrustConfig(basePath, verificationMode, keyConfig, Set.copyOf(trustRestrictionsX509Fields));

        if (protocols == null || protocols.isEmpty()) {
            throw new SslConfigException("no protocols configured in [" + settingPrefix + PROTOCOLS + "]");
        }
        if (ciphers == null || ciphers.isEmpty()) {
            throw new SslConfigException("no cipher suites configured in [" + settingPrefix + CIPHERS + "]");
        }
        final boolean isExplicitlyConfigured = hasSettings(settingPrefix);
        return new SslConfiguration(
            settingPrefix,
            isExplicitlyConfigured,
            trustConfig,
            keyConfig,
            verificationMode,
            clientAuth,
            ciphers,
            protocols,
            handshakeTimeoutMillis
        );
    }

View on GitHub (pinned to db6a809a66)

Solutions

  1. Remove the empty supported_protocols setting so DEFAULT_PROTOCOLS (TLSv1.2/TLSv1.3) applies.
  2. Populate the list with at least one valid protocol: supported_protocols: ["TLSv1.2","TLSv1.3"].
  3. Audit Helm/Ansible templates for conditional blocks that can render an empty array.

Example fix

# before
xpack.security.http.ssl.supported_protocols: []
# after (omit, or set explicitly)
xpack.security.http.ssl.supported_protocols: [ "TLSv1.2", "TLSv1.3" ]
Defensive patterns

Strategy: validation

Validate before calling

List<String> resolveProtocolsSafely(SslConfigurationLoader loader) {
    List<String> p = loader.resolveListSetting("supported_protocols", Function.identity(), SslConfigurationLoader.DEFAULT_PROTOCOLS);
    if (p == null || p.isEmpty()) return SslConfigurationLoader.DEFAULT_PROTOCOLS;
    return p;
}

Prevention

When it happens

Trigger: ssl.supported_protocols is explicitly set to an empty list (e.g. supported_protocols: []), or the resolver returns null because a custom loader subclass overrode DEFAULT_PROTOCOLS to null.

Common situations: YAML/JSON config with supported_protocols: [] intended to disable TLS; misconfigured Helm chart rendering an empty array; environment-specific override that blanks the list.

Related errors


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