{"record":{"id":"d3d7a7939a6f7fa5","repo":"hashicorp/nomad","slug":"stop-after-can-only-be-used-with-service-or-batch","errorCode":null,"errorMessage":"stop_after can only be used with service or batch job types","messagePattern":"stop_after can only be used with service or batch job types","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/group.go","lineNumber":29,"sourceCode":"\t\"github.com/hashicorp/go-multierror\"\n)\n\nconst (\n\t// ReconcileOption is used to specify the behavior of the reconciliation process\n\t// between the original allocations and the replacements when a previously\n\t// disconnected client comes back online.\n\tReconcileOptionKeepOriginal    = \"keep_original\"\n\tReconcileOptionKeepReplacement = \"keep_replacement\"\n\tReconcileOptionBestScore       = \"best_score\"\n\tReconcileOptionLongestRunning  = \"longest_running\"\n)\n\nvar (\n\t// Disconnect strategy validation errors\n\terrStopAndLost         = errors.New(\"Disconnect cannot be configured with both lost_after and stop_after\")\n\terrNegativeLostAfter   = errors.New(\"lost_after cannot be a negative duration\")\n\terrNegativeStopAfter   = errors.New(\"stop_after cannot be a negative duration\")\n\terrStopAfterNonService = errors.New(\"stop_after can only be used with service or batch job types\")\n\terrInvalidReconcile    = errors.New(\"reconcile option is invalid\")\n)\n\nfunc NewDefaultDisconnectStrategy() *DisconnectStrategy {\n\treturn &DisconnectStrategy{\n\t\tReplace:   new(true),\n\t\tReconcile: ReconcileOptionBestScore,\n\t}\n}\n\n// Disconnect strategy defines how both clients and server should behave in case of\n// disconnection between them.\ntype DisconnectStrategy struct {\n\t// Defines for how long the server will consider the unresponsive node as\n\t// disconnected but alive instead of lost.\n\tLostAfter time.Duration `mapstructure:\"lost_after\" hcl:\"lost_after,optional\"`\n\n\t// Defines for how long a disconnected client will keep its allocations running.","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/group.go#L11-L47","documentation":"Nomad's DisconnectStrategy allows configuring stop_after to stop tasks after a client disconnect, but this behavior is only supported for service and batch jobs. When a job declares stop_after while its Type is neither 'service' nor 'batch' (e.g. 'system'), Job.Validate appends errStopAfterNonService to the multierror. The error is a sentinel declared in nomad/structs/group.go and wrapped/compared by identity in tests.","triggerScenarios":"Submitting or validating a job whose block defines disconnect { stop_after = <duration> } while job.type is 'system' (or any type other than 'service' or 'batch'). Triggered inside Job.Validate via the multierror append at group.go:79.","commonSituations":"Template-ing a system job (e.g. a per-node monitoring task) and copying a disconnect block from a service job spec; upgrading workloads to Nomad versions that introduced disconnect strategies and applying the new fields without checking job type constraints.","solutions":["Change the job type to 'service' or 'batch' (e.g. type = \"service\") if stop_after semantics fit the workload","Remove the disconnect.stop_after field from the job specification","Use lost_after instead of stop_after if the job type cannot be changed and lost behavior is acceptable","Move the workload to a service/batch job where stop_after is supported"],"exampleFix":"# before\njob \"node-monitor\" {\n  type = \"system\"\n  disconnect {\n    stop_after = \"10m\"\n  }\n}\n\n# after\njob \"node-monitor\" {\n  type = \"service\"\n  disconnect {\n    stop_after = \"10m\"\n  }\n}","handlingStrategy":"validation","validationCode":"func validStopAfterType(jobType string) bool {\n\treturn jobType == \"service\" || jobType == \"batch\"\n}\nif job.Disconnect != nil && job.Disconnect.StopAfter > 0 && !validStopAfterType(job.Type) {\n\treturn fmt.Errorf(\"stop_after requires service or batch type; got %q\", job.Type)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep disconnect blocks only in service/batch job templates","Validate jobs with `nomad job validate` before submit","Never share disconnect config blocks across system and service jobs"],"tags":["nomad","job-validation","disconnect-strategy","config"],"backgroundTag":"invalid-job-spec","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"}