{"record":{"id":"7d2d00c31d92d247","repo":"hashicorp/nomad","slug":"missing-spread-attribute","errorCode":null,"errorMessage":"Missing spread attribute","messagePattern":"Missing spread attribute","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":10326,"sourceCode":"\tns := new(Spread)\n\t*ns = *s\n\n\tns.SpreadTarget = CopySliceSpreadTarget(s.SpreadTarget)\n\treturn ns\n}\n\nfunc (s *Spread) String() string {\n\tif s.str != \"\" {\n\t\treturn s.str\n\t}\n\ts.str = fmt.Sprintf(\"%s %s %v\", s.Attribute, s.SpreadTarget, s.Weight)\n\treturn s.str\n}\n\nfunc (s *Spread) Validate() error {\n\tvar mErr multierror.Error\n\tif s.Attribute == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing spread attribute\"))\n\t}\n\tif s.Weight <= 0 || s.Weight > 100 {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Spread block must have a positive weight from 0 to 100\"))\n\t}\n\tseen := make(map[string]struct{})\n\tsumPercent := uint32(0)\n\n\tfor _, target := range s.SpreadTarget {\n\t\t// Make sure there are no duplicates\n\t\t_, ok := seen[target.Value]\n\t\tif !ok {\n\t\t\tseen[target.Value] = struct{}{}\n\t\t} else {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Spread target value %q already defined\", target.Value))\n\t\t}\n\t\tif target.Percent > 100 {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Spread target percentage for value %q must be between 0 and 100\", target.Value))\n\t\t}","sourceCodeStart":10308,"sourceCodeEnd":10344,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L10308-L10344","documentation":"Spread.Validate() appends this error when the spread stanza's Attribute field is empty. Spread scheduling needs an attribute (node class, datacenter, meta key, etc.) to distribute allocations across its possible values; without it the spread cannot be evaluated and validation fails.","triggerScenarios":"Submitting a job/group spread stanza with weight and/or spread targets but no attribute, or constructing Spread{Weight: 50, SpreadTarget: ...} in Go leaving Attribute \"\" before Validate.","commonSituations":"Users copying spread examples that rely on even distribution and thinking attribute is optional; templated job generation that omits the attribute when only targets are customized; JSON API submissions missing the \"attribute\" key.","solutions":["Add an attribute to the spread stanza, e.g. spread { attribute = \"${node.datacenter}\" ... }.","If you want the referenced even-spread behavior, pick the node property you care about (${node.class}, ${meta.*}).","Remove the spread stanza if it was unintentional.","Run `nomad job validate` before submitting."],"exampleFix":"// before\nspread {\n  weight = 50\n}\n// after\nspread {\n  attribute = \"${node.datacenter}\"\n  weight    = 50\n}","handlingStrategy":"validation","validationCode":"func validateSpread(s *structs.Spread) error {\n\tif s.Attribute == \"\" {\n\t\treturn fmt.Errorf(\"spread stanza requires an attribute\")\n\t}\n\tif s.Weight <= 0 || s.Weight > 100 {\n\t\treturn fmt.Errorf(\"spread weight must be 1-100, got %d\", s.Weight)\n\t}\n\treturn nil\n}","typeGuard":"func isSpreadable(s *structs.Spread) bool { return s != nil && s.Attribute != \"\" && s.Weight > 0 && s.Weight <= 100 }","tryCatchPattern":null,"preventionTips":["Always specify attribute in spread stanzas (${node.datacenter}, ${node.class}, ${meta.*}).","Reject spread blocks without attribute in job template linting.","Test generated jobs with `nomad job validate`."],"tags":["nomad","spread","scheduling","validation"],"backgroundTag":"missing-required-argument","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"}