{"record":{"id":"d68bbaf9bc2ddc57","repo":"hashicorp/nomad","slug":"job-type-q-does-not-allow-update-block","errorCode":null,"errorMessage":"Job type %q does not allow update block","messagePattern":"Job type %q does not allow update block","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":7227,"sourceCode":"\t\t} else {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Task Group %v should have a reschedule policy\", tg.Name))\n\t\t}\n\t}\n\n\tif tg.EphemeralDisk != nil {\n\t\tif err := tg.EphemeralDisk.Validate(); err != nil {\n\t\t\tmErr = multierror.Append(mErr, err)\n\t\t}\n\t} else {\n\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Task Group %v should have an ephemeral disk object\", tg.Name))\n\t}\n\n\t// Validate the update strategy\n\tif u := tg.Update; u != nil {\n\t\tswitch j.Type {\n\t\tcase JobTypeService, JobTypeSystem:\n\t\tdefault:\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Job type %q does not allow update block\", j.Type))\n\t\t}\n\t\tif err := u.Validate(); err != nil {\n\t\t\tmErr = multierror.Append(mErr, err)\n\t\t}\n\t}\n\n\t// Validate the migration strategy\n\tswitch j.Type {\n\tcase JobTypeService:\n\t\tif tg.Migrate != nil {\n\t\t\tif err := tg.Migrate.Validate(); err != nil {\n\t\t\t\tmErr = multierror.Append(mErr, err)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tif tg.Migrate != nil {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Job type %q does not allow migrate block\", j.Type))\n\t\t}","sourceCodeStart":7209,"sourceCodeEnd":7245,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L7209-L7245","documentation":"Nomad's job validation (Job.Validate / TaskGroup.Validate in nomad/structs/structs.go) rejects an `update` block on a job whose type is neither \"service\" nor \"system\". Rolling-update/deployment strategy semantics only make sense for long-running job types, so batch, sysbatch, or custom-typed jobs carrying an `update` stanza are invalid. The message is appended to a multierror and surfaced with the rest of the job validation failures.","triggerScenarios":"Submitting (via jobs API/CLI `nomad job run`) a job spec where the `update` stanza is present in a task group but `job type = \"batch\"`, `\"sysbatch\"`, or any non-service/system type.","commonSituations":"Copying an update stanza from an existing service job into a batch job template; switching a job's type from service to batch after adding deployment automation; tooling that always emits an update block.","solutions":["Remove the `update` block from the task group in the job spec.","Or change the job's `type` to \"service\" if rolling updates are actually intended.","For batch jobs needing reschedule-like behavior, use `restart` and `reschedule` stanzas appropriate to the type instead."],"exampleFix":"// before\ntype = \"batch\"\nupdate {\n  max_parallel = 1\n}\n// after\ntype = \"batch\"\n# update block removed (not allowed for batch jobs)","handlingStrategy":"validation","validationCode":"// Go: pre-validate before submitting\nif job.Type != structs.JobTypeService && job.Type != structs.JobTypeSystem {\n  for _, tg := range job.TaskGroups {\n    if tg.Update != nil {\n      return fmt.Errorf(\"group %q: update block not allowed for job type %q\", tg.Name, job.Type)\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only emit update stanzas in templates for service/system jobs.","Run `nomad job validate` in CI before submission.","When retyping a job, audit group stanzas (update/migrate) for type compatibility."],"tags":["nomad","job-validation","update-block","hcl"],"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"}