hashicorp/nomad · error

KillTimout (%s) longer than the group's ProgressDeadline (%s

Error message

KillTimout (%s) longer than the group's ProgressDeadline (%s)

What it means

Raised during Task validation in nomad/structs/structs.go:8262 when a service job task's kill_timeout exceeds the task group's update block ProgressDeadline (and ProgressDeadline > 0). During a deployment, if a task fails health checks Nomad waits until the progress deadline before proceeding; a kill_timeout longer than that deadline makes the kill-then-restart sequence incoherent, so Nomad rejects it.

Source

Thrown at nomad/structs/structs.go:8262

		mErr.Errors = append(mErr.Errors, errors.New("Task name cannot include null characters"))
	}
	if t.Driver == "" {
		mErr.Errors = append(mErr.Errors, errors.New("Missing task driver"))
	}
	if t.KillTimeout < 0 {
		mErr.Errors = append(mErr.Errors, errors.New("KillTimeout must be a positive value"))
	} else {
		// Validate the group's update strategy does not conflict with the
		// task's kill_timeout for service jobs.
		//
		// progress_deadline = 0 has a special meaning so it should not be
		// validated against the task's kill_timeout.
		conflictsWithProgressDeadline := jobType == JobTypeService &&
			tg.Update != nil &&
			tg.Update.ProgressDeadline > 0 &&
			t.KillTimeout > tg.Update.ProgressDeadline
		if conflictsWithProgressDeadline {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("KillTimout (%s) longer than the group's ProgressDeadline (%s)",
				t.KillTimeout, tg.Update.ProgressDeadline))
		}
	}
	if t.ShutdownDelay < 0 {
		mErr.Errors = append(mErr.Errors, errors.New("ShutdownDelay must be a positive value"))
	}

	// Validate the resources.
	if t.Resources == nil {
		mErr.Errors = append(mErr.Errors, errors.New("Missing task resources"))
	} else if err := t.Resources.Validate(); err != nil {
		mErr.Errors = append(mErr.Errors, err)
	}

	// Validate the log config
	if t.LogConfig == nil {
		mErr.Errors = append(mErr.Errors, errors.New("Missing Log Config"))
	} else if err := t.LogConfig.Validate(tg.EphemeralDisk); err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Lower kill_timeout so it is <= update.progress_deadline.
  2. Raise update { progress_deadline } to be strictly greater than kill_timeout.
  3. Remove the custom kill_timeout to use the 5s default if graceful shutdown is not required.

Example fix

// before
update { progress_deadline = "5m" }
task "web" { kill_timeout = "10m" }
// after
update { progress_deadline = "15m" }
task "web" { kill_timeout = "10m" }
Defensive patterns

Strategy: validation

Validate before calling

if job.Type == "service" && tg.Update != nil && tg.Update.ProgressDeadline > 0 {
  for _, t := range tg.Tasks {
    if t.KillTimeout > tg.Update.ProgressDeadline {
      return fmt.Errorf("task %s: kill_timeout %s exceeds progress_deadline %s", t.Name, t.KillTimeout, tg.Update.ProgressDeadline)
    }
  }
}

Prevention

When it happens

Trigger: Service job with update { progress_deadline = 5m } and a task kill_timeout = 10m; calling tg.Validate/job Validate with t.KillTimeout > tg.Update.ProgressDeadline for JobTypeService.

Common situations: Users raise kill_timeout so graceful shutdown can finish but forget to extend progress_deadline; defaults changed in one place (kill_timeout = 30m) interacting with a small progress_deadline; deployments stuck then failing validation after a job edit.

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