{"record":{"id":"7e834d0b6eda4e05","repo":"hashicorp/nomad","slug":"sum-of-spread-target-percentages-must-not-be-great","errorCode":null,"errorMessage":"Sum of spread target percentages must not be greater than 100%%; got %d%%","messagePattern":"Sum of spread target percentages must not be greater than 100%%; got (.+?)%%","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":10348,"sourceCode":"\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}\n\t\tsumPercent += uint32(target.Percent)\n\t}\n\tif sumPercent > 100 {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Sum of spread target percentages must not be greater than 100%%; got %d%%\", sumPercent))\n\t}\n\treturn mErr.ErrorOrNil()\n}\n\n// SpreadTarget is used to specify desired percentages for each attribute value\ntype SpreadTarget struct {\n\t// Value is a single attribute value, like \"dc1\"\n\tValue string\n\n\t// Percent is the desired percentage of allocs\n\tPercent uint8\n\n\t// Memoized string representation\n\tstr string\n}\n\nfunc (s *SpreadTarget) Copy() *SpreadTarget {\n\tif s == nil {","sourceCodeStart":10330,"sourceCodeEnd":10366,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L10330-L10366","documentation":"Spread.Validate() sums all SpreadTarget percentages and rejects the job if the total exceeds 100 with 'Sum of spread target percentages must not be greater than 100%; got %d%%'. Remaining capacity (100 - sum) is implicitly spread evenly across non-targeted nodes.","triggerScenarios":"Registering a job where the spread targets sum to more than 100, e.g. dc1=60 and dc2=60 (sum 120), at job submission.","commonSituations":"Adding a new target without rebalancing existing ones, treating the sum as independent per-target budgets, or scripts that scale percentages incorrectly.","solutions":["Reduce target percentages so their total is <= 100.","Leave some percentage unallocated to allow spreading across other nodes.","Normalize the targets programmatically (scale each by 100/sum) before rendering."],"exampleFix":"// before\nspread {\n  attribute = \"${node.datacenter}\"\n  target \"dc1\" { percent = 60 }\n  target \"dc2\" { percent = 60 }\n}\n// after\nspread {\n  attribute = \"${node.datacenter}\"\n  target \"dc1\" { percent = 50 }\n  target \"dc2\" { percent = 50 }\n}","handlingStrategy":"validation","validationCode":"sum := uint32(0)\nfor _, t := range spread.SpreadTarget { sum += t.Percent }\nif sum > 100 { return fmt.Errorf(\"spread percentages sum to %d > 100\", sum) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep the sum of spread targets <= 100; leave headroom for other nodes","Rebalance all targets when adding a new one","Auto-normalize percentages in generated specs"],"tags":["nomad","spread","percent","job-spec","validation"],"backgroundTag":"invalid-parameter-range","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"}