elastic/elasticsearch · error · SslConfigException

handshake timeout must be at least 1ms

Error message

handshake timeout must be at least 1ms

What it means

SslConfiguration rejects handshakeTimeoutMillis < 1 because a zero/negative timeout would either disable handshake enforcement or break the scheduler. The field is the upper bound the TLS engine waits for a peer handshake.

Source

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

        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;
    }

    /**
     * @return A collection of files that are used by this SSL configuration. If the contents of these files change, then any
     * subsequent call to {@link #createSslContext()} (or similar methods) may create a context with different behaviour.
     * It is recommended that these files be monitored for changes, and a new ssl-context is created whenever any of the files are modified.
     */
    public Collection<Path> getDependentFiles() {
        Set<Path> paths = new HashSet<>(keyConfig.getDependentFiles());
        paths.addAll(trustConfig.getDependentFiles());

View on GitHub (pinned to db6a809a66)

Solutions

  1. Set ssl.handshake_timeout to a sensible positive duration (default is 10s), e.g. xpack.security.transport.ssl.handshake_timeout: 10s.
  2. If constructing programmatically, pass a value >= 1 (millis), e.g. Duration.ofSeconds(10).toMillis().
  3. Audit config templates for negative or zero time values across all ssl.* prefixes.

Example fix

// before
xpack.security.transport.ssl.handshake_timeout: 0
// after
xpack.security.transport.ssl.handshake_timeout: 10s
Defensive patterns

Strategy: validation

Validate before calling

long ensureHandshakeTimeout(org.elasticsearch.common.unit.TimeValue tv) {
    long ms = tv.millis();
    if (ms < 1L) throw new IllegalArgumentException("handshake_timeout must be >= 1ms, got " + tv);
    return ms;
}

Prevention

When it happens

Trigger: Constructing SslConfiguration with handshakeTimeoutMillis <= 0. In the loader path this happens when ssl.handshake_timeout parses (via TimeValue.parseTimeValue) to 0ms or a negative duration (e.g. "-5s", "0ms").

Common situations: Operator sets xpack.security.transport.ssl.handshake_timeout: 0 thinking it disables the timeout; negative value from a templated config; units typo producing a sub-millisecond value rounded to 0.

Understand the failure class

Related errors


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