{"record":{"id":"86be006b18c3f8d3","repo":"hashicorp/nomad","slug":"missing-affinity-operand","errorCode":null,"errorMessage":"Missing affinity operand","messagePattern":"Missing affinity operand","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":10197,"sourceCode":"\tif a == nil {\n\t\treturn nil\n\t}\n\treturn &Affinity{\n\t\tLTarget: a.LTarget,\n\t\tRTarget: a.RTarget,\n\t\tOperand: a.Operand,\n\t\tWeight:  a.Weight,\n\t}\n}\n\nfunc (a *Affinity) String() string {\n\treturn fmt.Sprintf(\"%s %s %s %v\", a.LTarget, a.Operand, a.RTarget, a.Weight)\n}\n\nfunc (a *Affinity) Validate() error {\n\tvar mErr multierror.Error\n\tif a.Operand == \"\" {\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"Missing affinity operand\"))\n\t}\n\n\t// Perform additional validation based on operand\n\tswitch a.Operand {\n\tcase ConstraintSetContainsAll, ConstraintSetContainsAny, ConstraintSetContains:\n\t\tif a.RTarget == \"\" {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Set contains operators require an RTarget\"))\n\t\t}\n\tcase ConstraintRegex:\n\t\tif _, err := regexp.Compile(a.RTarget); err != nil {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Regular expression failed to compile: %v\", err))\n\t\t}\n\tcase ConstraintVersion:\n\t\tif _, err := version.NewConstraint(a.RTarget); err != nil {\n\t\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"Version affinity is invalid: %v\", err))\n\t\t}\n\tcase ConstraintSemver:\n\t\tif _, err := semver.NewConstraint(a.RTarget); err != nil {","sourceCodeStart":10179,"sourceCodeEnd":10215,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L10179-L10215","documentation":"Affinity.Validate() appends this error when the affinity's Operand field is empty. Like constraints, affinities require an operator (e.g. =, regexp, set_contains, distinct_hosts) to interpret the scoring expression; a missing operand makes the affinity invalid and the job submission is rejected via the multierror.","triggerScenarios":"Submitting an affinity stanza with only attribute/value (and weight) but no operator, or constructing Affinity{LTarget, RTarget, Weight} in Go without Operand before calling Validate.","commonSituations":"Hand-written JSON job specs that omit the operator key; job-generation tooling mirroring constraint code that also skipped Operand; users porting an HCL constraint like `attr = \"value\"` (implicit '=' operand in HCL) into raw API calls.","solutions":["Set the affinity operand explicitly (affinity { attribute = ...; value = ... } in HCL implies '=', or Operand = \"=\" in Go).","Choose the intended operator (=, !=, regexp, version, set_contains, etc.) and populate it.","Remove the empty affinity stanza if it was accidental.","Validate locally with `nomad job validate`."],"exampleFix":"// before (Go)\na := &structs.Affinity{LTarget: \"${node.datacenter}\", RTarget: \"dc1\", Weight: 50}\n// after\na := &structs.Affinity{LTarget: \"${node.datacenter}\", Operand: \"=\", RTarget: \"dc1\", Weight: 50}","handlingStrategy":"validation","validationCode":"func validateAffinityOperand(a *structs.Affinity) error {\n\tif a.Operand == \"\" {\n\t\treturn fmt.Errorf(\"affinity %q: operand is required\", a.LTarget)\n\t}\n\treturn nil\n}","typeGuard":"func hasAffinityOperand(a *structs.Affinity) bool { return a != nil && a.Operand != \"\" }","tryCatchPattern":null,"preventionTips":["Default Operand to \"=\" when constructing affinities programmatically with LTarget/RTarget.","Mirror your constraint construction helpers so both paths enforce the operand.","Validate job specs in CI."],"tags":["nomad","affinity","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"}