{"record":{"id":"e8aa8341fc1a5b1b","repo":"hashicorp/nomad","slug":"task-group-s-must-have-at-least-one-main-task","errorCode":null,"errorMessage":"Task group %s must have at least one main task","messagePattern":"Task group (.+?) must have at least one main task","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":7277,"sourceCode":"\t\t}\n\n\t\tif task.Leader {\n\t\t\tleaderTasks++\n\t\t}\n\n\t\tif task.IsMain() {\n\t\t\tmainTasks++\n\t\t}\n\t}\n\n\tif leaderTasks > 1 {\n\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Only one task may be marked as leader\"))\n\t}\n\n\t// A task group made up entirely of lifecycle tasks (prestart, poststart, or\n\t// poststop) has no main task to run, which is invalid.\n\tif len(tg.Tasks) > 0 && mainTasks == 0 {\n\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Task group %s must have at least one main task\", tg.Name))\n\t}\n\n\t// Validate the volume requests\n\tvar canaries int\n\tif tg.Update != nil {\n\t\tcanaries = tg.Update.Canary\n\t}\n\tfor name, volReq := range tg.Volumes {\n\t\tif err := volReq.Validate(j.Type, tg.Count, canaries); err != nil {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\n\t\t\t\t\"Task group volume validation for %s failed: %v\", name, err))\n\t\t}\n\t}\n\n\t// Validate task group and task network resources\n\tif err := tg.validateNetworks(); err != nil {\n\t\touter := fmt.Errorf(\"Task group network validation failed: %v\", err)\n\t\tmErr = multierror.Append(mErr, outer)","sourceCodeStart":7259,"sourceCodeEnd":7295,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L7259-L7295","documentation":"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.","triggerScenarios":"Submitting a group whose tasks all set `lifecycle { hook = \"prestart\"/\"poststart\"/\"poststop\" }` with no task left as a main (non-lifecycle) task.","commonSituations":"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.","solutions":["Add a main task (a task without a lifecycle stanza) to the group.","Or remove the orphaned lifecycle-only tasks and attach them as sidecars to a group that has a main task."],"exampleFix":"// before\ngroup \"g\" {\n  task \"init\" {\n    lifecycle { hook = \"prestart\" }\n  }\n}\n// after\ngroup \"g\" {\n  task \"init\" {\n    lifecycle { hook = \"prestart\" }\n  }\n  task \"app\" { driver = \"docker\" }\n}","handlingStrategy":"validation","validationCode":"// Go: ensure each non-empty group has a main task\nfor _, tg := range job.TaskGroups {\n  if len(tg.Tasks) == 0 {\n    continue\n  }\n  mains := 0\n  for _, t := range tg.Tasks {\n    if !t.IsMain() {\n      mains++\n    }\n  }\n  if mains == 0 {\n    return fmt.Errorf(\"group %q has no main task\", tg.Name)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pair every lifecycle (prestart/poststart/poststop) task with a main task in the same group.","Audit groups after moving tasks between groups.","Validate specs in CI with `nomad job validate`."],"tags":["nomad","job-validation","lifecycle-hooks"],"backgroundTag":"nomad-job-validation-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}