hashicorp/nomad · error
rpc_handshake_timeout must be >= 0
Error message
rpc_handshake_timeout must be >= 0
What it means
After parsing limits.rpc_handshake_timeout, convertServerConfig rejects negative durations: a negative handshake timeout is meaningless (a TCP/TLS handshake can't complete in negative time), so the agent refuses to start or reload rather than silently misconfiguring the RPC layer.
Source
Thrown at command/agent/agent.go:598
}
if agentConfig.RPC.DialTimeout > 0 {
conf.RPCDialTimeout = agentConfig.RPC.DialTimeout
}
}
// Set the TLS config
conf.TLSConfig = agentConfig.TLSConfig
// Setup telemetry related config
conf.StatsCollectionInterval = agentConfig.Telemetry.collectionInterval
conf.DisableDispatchedJobSummaryMetrics = agentConfig.Telemetry.DisableDispatchedJobSummaryMetrics
conf.DisableQuotaUtilizationMetrics = agentConfig.Telemetry.DisableQuotaUtilizationMetrics
conf.DisableRPCRateMetricsLabels = agentConfig.Telemetry.DisableRPCRateMetricsLabels
if d, err := time.ParseDuration(agentConfig.Limits.RPCHandshakeTimeout); err != nil {
return nil, fmt.Errorf("error parsing rpc_handshake_timeout: %v", err)
} else if d < 0 {
return nil, fmt.Errorf("rpc_handshake_timeout must be >= 0")
} else {
conf.RPCHandshakeTimeout = d
}
// Set max rpc conns; nil/0 == unlimited
// Leave a little room for streaming RPCs
minLimit := config.LimitsNonStreamingConnsPerClient + 5
if agentConfig.Limits.RPCMaxConnsPerClient == nil || *agentConfig.Limits.RPCMaxConnsPerClient == 0 {
conf.RPCMaxConnsPerClient = 0
} else if limit := *agentConfig.Limits.RPCMaxConnsPerClient; limit <= minLimit {
return nil, fmt.Errorf("rpc_max_conns_per_client must be > %d; found: %d", minLimit, limit)
} else {
conf.RPCMaxConnsPerClient = limit
}
// Set deployment rate limit
if rate := agentConfig.Server.DeploymentQueryRateLimit; rate == 0 {
conf.DeploymentQueryRateLimit = deploymentwatcher.LimitStateQueriesPerSecondView on GitHub (pinned to 482b49bf1a)
Solutions
- Set a non-negative duration, e.g. rpc_handshake_timeout = "5s"
- Remove the setting to use Nomad's default handshake timeout
- If you wanted to disable the limit, check Nomad docs for the supported mechanism instead of using a negative value
- Fix any template/variable producing a negative number
Example fix
// before
limits {
rpc_handshake_timeout = "-1s"
}
// after
limits {
rpc_handshake_timeout = "5s"
} Defensive patterns
Strategy: validation
Validate before calling
// Go: reject negative durations before applying config
d, err := time.ParseDuration(cfg.Limits.RPCHandshakeTimeout)
if err != nil || d < 0 {
return errors.New("rpc_handshake_timeout must be a non-negative duration")
} Type guard
func isNonNegativeDuration(s string) bool {
d, err := time.ParseDuration(s)
return err == nil && d >= 0
} Prevention
- Never use negative values to 'disable' timeouts; remove the option instead
- Clamp generated/templated values to >= 0
- Add config lint checks for numeric sign on duration fields
When it happens
Trigger: Setting limits { rpc_handshake_timeout = "-1s" } (or any negative duration) in the server agent config and starting the agent, or applying it via a reload handled by handleReload.
Common situations: Operators trying to 'disable' the timeout with a negative value; templated configs where an interpolated variable resolves to a negative number; copy-paste mistakes from tuning scripts.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- wait config is nil or empty
- missing datacenter for client registration
- default_identity_ttl must be greater than 0
- max_identity_ttl must be greater than 0
- max_identity_ttl must be greater than or equal to default_id
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/ceef56679436c8c5.
Report an issue: GitHub.