hashicorp/nomad · error

client_introduction.default_identity_ttl must be greater one

Error message

client_introduction.default_identity_ttl must be greater one

What it means

Within the client_introduction validation, `default_identity_ttl` must be at least 1 (interpreted as a duration; <1 is nonsensical). Validate() rejects values below 1 before checking max_identity_ttl relationships. This guarantees the agent never configures a zero or negative default identity lifetime.

Source

Thrown at command/agent/config.go:1269

// Validate performs validation on the ClientIntroduction configuration block to
// ensure the values are set correctly for use by the server.
func (c *ClientIntroduction) Validate() error {

	if c == nil {
		return nil
	}

	if c.Enforcement == "" {
		return errors.New("client_introduction.enforcement must be set")
	}
	if !slices.Contains(ClientIntroductionEnforcementValues, c.Enforcement) {
		return fmt.Errorf("client_introduction.enforcement must be one of %v",
			ClientIntroductionEnforcementValues)
	}

	if c.DefaultIdentityTTL < 1 {
		return errors.New("client_introduction.default_identity_ttl must be greater one")
	}
	if c.MaxIdentityTTL < 1 {
		return errors.New("client_introduction.max_identity_ttl must be greater one")
	}
	if c.MaxIdentityTTL < c.DefaultIdentityTTL {
		return errors.New("client_introduction.max_identity_ttl must be greater than default_identity_ttl")
	}

	return nil
}

// ServerJoin is used in both clients and servers to bootstrap connections to
// servers
type ServerJoin struct {
	// StartJoin is a list of addresses to attempt to join when the
	// agent starts. If Serf is unable to communicate with any of these
	// addresses, then the agent will error and exit.
	StartJoin []string `hcl:"start_join"`

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set `default_identity_ttl` to a positive duration, e.g. "1h".
  2. If you intended 'no expiry', use the maximum allowed TTL value instead of 0.
  3. Verify the duration unit suffix is present in the config so the parsed value is not near-zero.

Example fix

// before
client_introduction {
  enforcement          = "serve-and-mark"
  default_identity_ttl = 0
}

// after
client_introduction {
  enforcement          = "serve-and-mark"
  default_identity_ttl = "1h"
}
Defensive patterns

Strategy: validation

Validate before calling

if c := cfg.ClientIntroduction; c != nil && c.DefaultIdentityTTL < 1 {
    return errors.New("client_introduction.default_identity_ttl must be >= 1")
}

Type guard

func validDefaultTTL(c *ClientIntroduction) bool { return c != nil && c.DefaultIdentityTTL >= 1 }

Prevention

When it happens

Trigger: Config sets `client_introduction.default_identity_ttl` to 0 or a negative number (or the parsed duration is < 1) while the block is present, tripping `if c.DefaultIdentityTTL < 1`.

Common situations: Setting the TTL to 0 intending 'never expire' or 'inherit'; unit/test configs with placeholder values; unit confusion (seconds vs nanoseconds) making an intended positive value parse as near-zero.

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/ae35b15d58101966. Report an issue: GitHub.