hashicorp/nomad · error
retry_join and start_join cannot both be defined
Error message
retry_join and start_join cannot both be defined
What it means
When the deprecated top-level server retry_join is defined, retryJoiner.Validate also ensures server_join.start_join is not simultaneously set — automatic retry joining and manual one-shot start joining are mutually exclusive in the same stanza.
Source
Thrown at command/agent/retry_join.go:143
// If retry_join is defined for the server, ensure that deprecated
// fields and the server_join block are not both set
if config.Server != nil && config.Server.ServerJoin != nil && len(config.Server.ServerJoin.RetryJoin) != 0 {
if len(config.Server.RetryJoin) != 0 {
return fmt.Errorf("server_join and retry_join cannot both be defined; prefer setting the server_join block")
}
if len(config.Server.StartJoin) != 0 {
return fmt.Errorf("server_join and start_join cannot both be defined; prefer setting the server_join block")
}
if config.Server.RetryMaxAttempts != 0 {
return fmt.Errorf("server_join and retry_max cannot both be defined; prefer setting the server_join block")
}
if config.Server.RetryInterval != 0 {
return fmt.Errorf("server_join and retry_interval cannot both be defined; prefer setting the server_join block")
}
if len(config.Server.ServerJoin.StartJoin) != 0 {
return fmt.Errorf("retry_join and start_join cannot both be defined")
}
}
// if retry_join is defined for the client, ensure that start_join is not
// set as this configuration is only defined for servers.
if config.Client != nil && config.Client.ServerJoin != nil {
if config.Client.ServerJoin.StartJoin != nil {
return fmt.Errorf("start_join is not supported for Nomad clients")
}
}
return nil
}
// RetryJoin is used to handle retrying a join until it succeeds or all retries
// are exhausted.
func (r *retryJoiner) RetryJoin() {
if len(r.joinCfg.RetryJoin) == 0 {View on GitHub (pinned to 482b49bf1a)
Solutions
- Pick one join strategy: remove server_join.start_join to use retry_join, or remove retry_join entries to use start_join
- Keep the server_join block as the single place configuring join behavior
- Validate the final config with nomad config validate
Example fix
// before
server {
retry_join = ["10.0.0.1"]
server_join {
start_join = ["10.0.0.2"]
}
}
// after
server {
server_join {
retry_join = ["10.0.0.1"]
}
} Defensive patterns
Strategy: validation
Validate before calling
if s := cfg.Server; s != nil && (len(s.RetryJoin) > 0 || (s.ServerJoin != nil && len(s.ServerJoin.RetryJoin) > 0)) && s.ServerJoin != nil && len(s.ServerJoin.StartJoin) > 0 {
return errors.New("retry_join and start_join are mutually exclusive")
} Prevention
- Choose one join mechanism per stanza: retry_join (automatic) or start_join (manual bootstrap)
- Keep all join settings inside the server_join block
- Validate merged config fragments from multiple environments
- Run nomad config validate before deploys
When it happens
Trigger: Config where server.retry_join (or server_join.retry_join) is non-empty AND server.server_join.start_join is non-empty; Validate fails during startup.
Common situations: Operators combining a static bootstrap list (start_join) with dynamic cloud auto-join (retry_join) without realizing they conflict; merging config fragments from different environments.
Related errors
- Reschedule policy has unlimited attempts enabled and a low d
- Lock delay and TTL must be positive
- https_handshake_timeout must be >= 0
- http_max_conns_per_client must be >= 0
- server_join and retry_join cannot both be defined; prefer se
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/bb07dcae5475b75f.
Report an issue: GitHub.