hashicorp/nomad · error

ShutdownDelay must be a positive value

Error message

ShutdownDelay must be a positive value

What it means

Nomad job validation rejects a task group whose ShutdownDelay is negative. ShutdownDelay is the time Nomad waits after a task exits before stopping the next group during a rolling update; it must be a positive (well, non-negative; strictly >= 0, error fires only for < 0) duration. It is raised inside Job validation's task-group dedup/validation loop when *tg.ShutdownDelay < 0.

Source

Thrown at nomad/structs/structs.go:4836

	if j.VersionTag != nil {
		if len(j.VersionTag.Description) > MaxDescriptionCharacters {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Tagged version description must be under 1000 characters, currently %d", len(j.VersionTag.Description)))
		}
	}

	// Check for duplicate task groups
	taskGroups := make(map[string]int)
	for idx, tg := range j.TaskGroups {
		if tg.Name == "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Job task group %d missing name", idx+1))
		} else if existing, ok := taskGroups[tg.Name]; ok {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Job task group %d redefines '%s' from group %d", idx+1, tg.Name, existing+1))
		} else {
			taskGroups[tg.Name] = idx
		}

		if tg.ShutdownDelay != nil && *tg.ShutdownDelay < 0 {
			mErr.Errors = append(mErr.Errors, errors.New("ShutdownDelay must be a positive value"))
		}

		if j.Type == "system" && tg.Count > 1 {
			mErr.Errors = append(mErr.Errors,
				fmt.Errorf("Job task group %s has count %d. Count cannot exceed 1 with system scheduler",
					tg.Name, tg.Count))
		}
	}

	// Validate the task group
	for _, tg := range j.TaskGroups {
		if err := tg.Validate(j); err != nil {
			outer := fmt.Errorf("Task group %s validation failed: %v", tg.Name, err)
			mErr.Errors = append(mErr.Errors, outer)
		}
	}

	// Validate periodic is only used with batch or sysbatch jobs.

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set shutdown_delay to a positive duration value in the task group (e.g. "5s").
  2. Remove the shutdown_delay field entirely to use the zero default.
  3. Check templated/env-provided values before injecting them into the spec.

Example fix

// before
group "web" {
  shutdown_delay = "-5s"
}
// after
group "web" {
  shutdown_delay = "5s"
}
Defensive patterns

Strategy: validation

Validate before calling

if tg.ShutdownDelay != nil && tg.ShutdownDelay.Seconds() < 0 {
    return fmt.Errorf("shutdown_delay must be >= 0, got %s", tg.ShutdownDelay)
}

Type guard

func validShutdownDelay(d *time.Duration) bool { return d == nil || *d >= 0 }

Prevention

When it happens

Trigger: Submitting a job spec (nomad job run / API Job Validate) where a group sets shutdown_delay to a negative duration, e.g. shutdown_delay = "-5s".

Common situations: Hand-editing HCL and prefixing the value with a minus sign; templating the delay from a computed value that can go negative; misreading the field as a signed offset.

Related errors


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