hashicorp/nomad · error

Task group %s must have at least one main task

Error message

Task group %s must have at least one main task

What it means

TaskGroup.Validate requires that a non-empty group contain at least one main task. A group composed entirely of lifecycle-hook tasks (prestart, poststart, poststop) has nothing to run as the primary workload, which is invalid; this error is appended when mainTasks == 0 for a group with tasks.

Source

Thrown at nomad/structs/structs.go:7277

		}

		if task.Leader {
			leaderTasks++
		}

		if task.IsMain() {
			mainTasks++
		}
	}

	if leaderTasks > 1 {
		mErr = multierror.Append(mErr, fmt.Errorf("Only one task may be marked as leader"))
	}

	// A task group made up entirely of lifecycle tasks (prestart, poststart, or
	// poststop) has no main task to run, which is invalid.
	if len(tg.Tasks) > 0 && mainTasks == 0 {
		mErr = multierror.Append(mErr, fmt.Errorf("Task group %s must have at least one main task", tg.Name))
	}

	// Validate the volume requests
	var canaries int
	if tg.Update != nil {
		canaries = tg.Update.Canary
	}
	for name, volReq := range tg.Volumes {
		if err := volReq.Validate(j.Type, tg.Count, canaries); err != nil {
			mErr = multierror.Append(mErr, fmt.Errorf(
				"Task group volume validation for %s failed: %v", name, err))
		}
	}

	// Validate task group and task network resources
	if err := tg.validateNetworks(); err != nil {
		outer := fmt.Errorf("Task group network validation failed: %v", err)
		mErr = multierror.Append(mErr, outer)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Add a main task (a task without a lifecycle stanza) to the group.
  2. Or remove the orphaned lifecycle-only tasks and attach them as sidecars to a group that has a main task.

Example fix

// before
group "g" {
  task "init" {
    lifecycle { hook = "prestart" }
  }
}
// after
group "g" {
  task "init" {
    lifecycle { hook = "prestart" }
  }
  task "app" { driver = "docker" }
}
Defensive patterns

Strategy: validation

Validate before calling

// Go: ensure each non-empty group has a main task
for _, tg := range job.TaskGroups {
  if len(tg.Tasks) == 0 {
    continue
  }
  mains := 0
  for _, t := range tg.Tasks {
    if !t.IsMain() {
      mains++
    }
  }
  if mains == 0 {
    return fmt.Errorf("group %q has no main task", tg.Name)
  }
}

Prevention

When it happens

Trigger: Submitting a group whose tasks all set `lifecycle { hook = "prestart"/"poststart"/"poststop" }` with no task left as a main (non-lifecycle) task.

Common situations: Refactoring a job into init-container style helpers and forgetting the main workload; moving the only main task into another group while leaving its lifecycle sidecars behind.

Related errors


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