hashicorp/nomad · error

server_join and start_join cannot both be defined; prefer se

Error message

server_join and start_join cannot both be defined; prefer setting the server_join block

What it means

Same retryJoiner.Validate consistency check: the server_join block's retry_join is defined alongside the deprecated top-level start_join field. Nomad rejects the ambiguous configuration in favor of the server_join block.

Source

Thrown at command/agent/retry_join.go:132

	// on the agent mode.
	joinFunc func([]string) (int, error)

	// logger is the retry joiners logger
	logger log.Logger
}

// Validate ensures that the configuration passes validity checks for the
// retry_join block. If the configuration is not valid, returns an error that
// will be displayed to the operator, otherwise nil.
func (r *retryJoiner) Validate(config *Config) error {
	// 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 {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Remove server.start_join and use server_join { start_join } or server_join { retry_join } instead
  2. Decide whether joining should be automatic (retry_join) or one-shot manual (start_join) and keep only one mechanism
  3. Run nomad config validate to confirm the conflict is resolved

Example fix

// before
server {
  start_join = ["1.2.3.4"]
  server_join {
    retry_join = ["1.2.3.4"]
  }
}
// after
server {
  server_join {
    retry_join = ["1.2.3.4"]
  }
}
Defensive patterns

Strategy: validation

Validate before calling

if s := cfg.Server; s != nil && s.ServerJoin != nil && len(s.ServerJoin.RetryJoin) > 0 && len(s.StartJoin) > 0 {
    return errors.New("server_join.retry_join and server.start_join are mutually exclusive")
}

Prevention

When it happens

Trigger: Config where server.server_join.retry_join is non-empty AND server.start_join is non-empty; Validate fails at agent startup.

Common situations: Mixing legacy start_join (manual bootstrap join list) with the newer server_join retry_join during version upgrades; templated configs accumulating both keys over time.

Related errors


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