{"record":{"id":"c1aeb959bfa82562","repo":"hashicorp/nomad","slug":"attribute-q-is-missing-an-opening-brace","errorCode":null,"errorMessage":"attribute %q is missing an opening brace","messagePattern":"attribute %q is missing an opening brace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/constraint.go","lineNumber":44,"sourceCode":"\t\t\"${device.\",\n\t\t\"${meta.\",\n\t}\n)\n\n// validateConstraintAttribute ensures the constraint attribute is valid. It\n// does this by ensuring any interpolated field can be handled by the\n// resolveTarget function.\nfunc validateConstraintAttribute(target string) error {\n\n\t// If no prefix delimieter is included, we assume this is a literal value\n\t// and is therefore valid.\n\tif !strings.HasPrefix(target, \"$\") {\n\t\treturn nil\n\t}\n\n\t// Must have the correct opening and closing delimeters.\n\tif !strings.HasPrefix(target, \"${\") {\n\t\treturn fmt.Errorf(\"attribute %q is missing an opening brace\", target)\n\t}\n\tif !strings.HasSuffix(target, \"}\") {\n\t\treturn fmt.Errorf(\"attribute %q is missing a closing brace\", target)\n\t}\n\n\t// Perform our exact target matching first. If the target does not hit this\n\t// exact match, we will fall through to the prefix match check.\n\tif slices.Contains(validConstraintExactTargets, target) {\n\t\treturn nil\n\t}\n\n\t// Check the target against our valid prefixes.\n\tfor _, prefix := range validConstraintPrefixTargets {\n\t\tif strings.HasPrefix(target, prefix) {\n\t\t\treturn nil\n\t\t}\n\t}\n","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/constraint.go#L26-L62","documentation":"This error comes from Nomad's constraint attribute validation (validateConstraintAttribute). Constraint targets can be an attribute path or an interpolation expression. If a target starts with '$' but is not exactly '${...}', the opening brace is missing, so the constraint is rejected. It exists to catch malformed interpolation syntax early at job validation time.","triggerScenarios":"Registering or validating a job whose constraint 'attribute' field starts with '$' but not '${', e.g. attribute=\"$node.class\" or attribute=\"$attr\" instead of \"${node.class}\" / \"${attr...}\".","commonSituations":"Hand-written job specs where the author forgot the opening brace after '$'; typos when converting from older HCL interpolation styles; copy-paste errors dropping '{'.","solutions":["Wrap the variable name in braces: change \"$node.class\" to \"${node.class}\".","If you meant an exact target, use one of the valid literal targets (e.g. \"${node.unique.id}\") with full ${...} syntax.","Run nomad job validate locally to catch the malformed attribute before submitting."],"exampleFix":"// before\nconstraint {\n  attribute = \"$node.class\"\n  operator  = \"=\"\n  value     = \"linux\"\n}\n// after\nconstraint {\n  attribute = \"${node.class}\"\n  operator  = \"=\"\n  value     = \"linux\"\n}","handlingStrategy":"validation","validationCode":"func validConstraintTarget(target string) bool {\n  if !strings.HasPrefix(target, \"$\") { return true }\n  return strings.HasPrefix(target, \"${\") && strings.HasSuffix(target, \"}\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use full ${...} interpolation syntax for constraint attributes.","Run `nomad job validate` in CI before submitting job specs.","Lint HCL templates for unbalanced braces on '$' expressions."],"tags":["nomad","constraint","interpolation","validation"],"backgroundTag":"malformed-interpolation-syntax","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"}