hashicorp/nomad · error

https_handshake_timeout must be >= 0

Error message

https_handshake_timeout must be >= 0

What it means

Returned by NewHTTPServers (command/agent/http.go:135) when `limits.https_handshake_timeout` parses as a duration but is negative. A negative TLS handshake timeout is nonsensical, so the agent refuses to start the HTTP servers.

Source

Thrown at command/agent/http.go:135

	wsUpgrader *websocket.Upgrader
}

// NewHTTPServers starts an HTTP server for every address.http configured in
// the agent.
func NewHTTPServers(agent *Agent, config *Config) ([]*HTTPServer, error) {
	var (
		srvs                       []*HTTPServer
		serverInitializationErrors error
		connCount                  atomic.Int32
	)

	// Get connection handshake timeout limit
	handshakeTimeout, err := time.ParseDuration(config.Limits.HTTPSHandshakeTimeout)
	if err != nil {
		return srvs, fmt.Errorf("error parsing https_handshake_timeout: %v", err)
	} else if handshakeTimeout < 0 {
		return srvs, fmt.Errorf("https_handshake_timeout must be >= 0")
	}

	// Get max connection limit
	maxConns := 0
	if mc := config.Limits.HTTPMaxConnsPerClient; mc != nil {
		maxConns = *mc
	}
	if maxConns < 0 {
		return srvs, fmt.Errorf("http_max_conns_per_client must be >= 0")
	}

	tlsConf, err := tlsutil.NewTLSConfiguration(config.TLSConfig, config.TLSConfig.VerifyHTTPSClient, true)
	if err != nil && config.TLSConfig.EnableHTTP {
		return srvs, fmt.Errorf("failed to initialize HTTP server TLS configuration: %s", err)
	}

	wsUpgrader := &websocket.Upgrader{
		ReadBufferSize:  2048,

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set a non-negative duration, e.g. https_handshake_timeout = "5s".
  2. Remove the setting entirely to use defaults if the value was a negative sentinel.
  3. Run `nomad validate` on the config before starting the agent.
  4. If reload triggered it, correct the config and reload again.

Example fix

// before (HCL)
limits {
  https_handshake_timeout = "-1s"
}
// after
limits {
  https_handshake_timeout = "5s"
}
Defensive patterns

Strategy: validation

Validate before calling

d, err := time.ParseDuration(cfg.Limits.HTTPSHandshakeTimeout)
if err != nil || d < 0 {
    return fmt.Errorf("https_handshake_timeout must be a non-negative duration")
}

Type guard

func validHandshakeTimeout(v string) bool {
    d, err := time.ParseDuration(v)
    return err == nil && d >= 0
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "https_handshake_timeout must be >= 0") {
        // replace negative sentinel with default by removing the setting
    }
}

Prevention

When it happens

Trigger: Setting `limits { https_handshake_timeout = "-1s" }` or any negative duration, then starting or reloading the agent's HTTP server.

Common situations: Sign typos; templating interpolating a negative sentinel meaning 'unset'; operators attempting to disable the timeout with a negative number instead of removing the setting.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/31caf98f4015f7d8. Report an issue: GitHub.