{"record":{"id":"9679ca9bfd105597","repo":"hashicorp/nomad","slug":"job-datacenter-must-be-non-empty-string","errorCode":null,"errorMessage":"Job datacenter must be non-empty string","messagePattern":"Job datacenter must be non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":4771,"sourceCode":"\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job Name contains a null character\"))\n\t}\n\n\tif j.Namespace == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job must be in a namespace\"))\n\t}\n\tswitch j.Type {\n\tcase JobTypeCore, JobTypeService, JobTypeBatch, JobTypeSystem, JobTypeSysBatch:\n\tcase \"\":\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job type\"))\n\tdefault:\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Invalid job type: %q\", j.Type))\n\t}\n\tif len(j.Datacenters) == 0 && !j.IsMultiregion() {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job datacenters\"))\n\t} else {\n\t\tfor _, v := range j.Datacenters {\n\t\t\tif v == \"\" {\n\t\t\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Job datacenter must be non-empty string\"))\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(j.TaskGroups) == 0 {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing job task groups\"))\n\t}\n\tfor idx, constr := range j.Constraints {\n\t\tif err := constr.Validate(); err != nil {\n\t\t\touter := fmt.Errorf(\"Constraint %d validation failed: %s\", idx+1, err)\n\t\t\tmErr.Errors = append(mErr.Errors, outer)\n\t\t}\n\t}\n\tif j.Type == JobTypeSystem {\n\t\tif j.Affinities != nil {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"System jobs may not have an affinity block\"))\n\t\t}\n\t} else {","sourceCodeStart":4753,"sourceCodeEnd":4789,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L4753-L4789","documentation":"When j.Datacenters is non-empty, Job.Validate() iterates the list and rejects any empty-string entry. Every datacenter name must be a non-empty string so the scheduler can match it against node datacenter labels.","triggerScenarios":"Submitting a job with datacenters = [\"dc1\", \"\"] or with a template variable that renders empty inside the list; constructing structs.Job with an empty-string element in Datacenters.","commonSituations":"Templating like datacenters = [\"${dc}\"] where ${dc} resolves to \"\"; trailing commas or whitespace-only entries after string processing; merging config files that append an empty entry.","solutions":["Remove empty entries from the datacenters list, keeping only valid names","Fix the template/variable that renders empty and add a non-empty guard","Filter with a helper before submission (keep only trimmed non-empty values)"],"exampleFix":"// before\ndatacenters = [\"dc1\", \"${extra_dc}\"]\n// after\ndatacenters = [\"dc1\"]\n// or ensure ${extra_dc} resolves to a real datacenter name","handlingStrategy":"validation","validationCode":"cleaned := make([]string, 0, len(j.Datacenters))\nfor _, dc := range j.Datacenters {\n    if strings.TrimSpace(dc) != \"\" { cleaned = append(cleaned, dc) }\n}\nj.Datacenters = cleaned","typeGuard":"func hasValidDatacenters(dcs []string) bool {\n    for _, dc := range dcs { if strings.TrimSpace(dc) == \"\" { return false } }\n    return len(dcs) > 0\n}","tryCatchPattern":"if err := job.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"datacenter must be non-empty\") {\n        return fmt.Errorf(\"remove empty entries from the datacenters list: %w\", err)\n    }\n}","preventionTips":["Filter templated datacenter lists for empty renders before submission","Avoid trailing commas / whitespace-only entries in hand-written lists","Validate templates so variables feeding datacenters never resolve empty"],"tags":["nomad","go","validation","scheduling"],"backgroundTag":"invalid-list-entry","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"}