hashicorp/nomad · error

server_join and retry_join cannot both be defined; prefer se

Error message

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

What it means

Nomad's retryJoiner.Validate checks agent configuration consistency. The newer server_join block and the deprecated top-level retry_join field were both set for the server stanza, which is ambiguous; Nomad rejects the config and asks operators to prefer server_join.

Source

Thrown at command/agent/retry_join.go:129

	joinCfg *ServerJoin

	// joinFunc is the function which executes the join process and is dependent
	// 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

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Remove the deprecated retry_join field from the server stanza, keeping only server_join { retry_join }
  2. Migrate all related fields at once: start_join, retry_max, retry_interval also have server_join equivalents
  3. Run 'nomad agent -config' validation (nomad config validate) after editing to catch remaining conflicts

Example fix

// before
server {
  retry_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.RetryJoin) > 0 {
    return errors.New("server_join.retry_join and server.retry_join are mutually exclusive")
}

Try / catch

if err := agent.ValidateConfig(cfg); err != nil {
	if strings.Contains(err.Error(), "cannot both be defined") {
		// fix config, do not retry
		return fmt.Errorf("config error: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Agent config HCL/JSON where server { server_join { retry_join = [...] } } and server { retry_join = [...] } are both non-empty; Validate is called during agent startup config parsing (via handleRetryJoin).

Common situations: Upgrading from older Nomad versions (pre server_join) where retry_join was top-level, then adding a server_join block without removing the legacy key; copying example configs that mix old and new syntax.

Related errors


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