{"record":{"id":"ece5c90394283bea","repo":"hashicorp/nomad","slug":"empty-variables-are-invalid","errorCode":null,"errorMessage":"empty variables are invalid","messagePattern":"empty variables are invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/variables.go","lineNumber":375,"sourceCode":"}\n\nvar (\n\t// validVariablePath is used to validate a variable path. We restrict to\n\t// RFC3986 URL-safe characters that don't conflict with the use of\n\t// characters \"@\" and \".\" in template blocks. We also restrict the length so\n\t// that a user can't make queries in the state store unusually expensive (as\n\t// they are O(k) on the key length)\n\tvalidVariablePath = regexp.MustCompile(\"^[a-zA-Z0-9-_~/]{1,128}$\")\n)\n\nfunc (vd VariableDecrypted) Validate() error {\n\n\tif vd.Namespace == AllNamespacesSentinel {\n\t\treturn errors.New(\"can not target wildcard (\\\"*\\\")namespace\")\n\t}\n\n\tif len(vd.Items) == 0 {\n\t\treturn errors.New(\"empty variables are invalid\")\n\t}\n\n\tif vd.Items.Size() > maxVariableSize {\n\t\treturn errors.New(\"variables are limited to 64KiB in total size\")\n\t}\n\n\tif err := ValidatePath(vd.Path); err != nil {\n\t\treturn err\n\t}\n\n\tif vd.Lock != nil {\n\t\treturn vd.Lock.Validate()\n\t}\n\n\treturn nil\n}\n\n// ValidateForLock ensures a new variable can be created just to support a lock,","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/variables.go#L357-L393","documentation":"VariableDecrypted.Validate rejects variables whose Items map is empty. A variable must carry at least one key/value pair; an empty payload is considered invalid because it cannot represent meaningful variable data and would leave an unusable raft entry. This is a distinct inline errors.New at nomad/structs/variables.go:375.","triggerScenarios":"Calling Validate on a VariableDecrypted with nil or zero-length Items map; e.g. nomad var put with no key=value arguments, or a template that renders an empty item set.","commonSituations":"Scripted variable writes where all keys were filtered out; deleting the last key by putting an empty set instead of using the delete API; JSON payloads with an empty \"Items\" or \"VariableKVDict\" object.","solutions":["Include at least one key/value pair in Items before saving.","If you intend to remove the variable, use the variable delete API instead of writing an empty variable.","Validate Items length client-side and fail fast with a clear message when empty."],"exampleFix":"// before\nv := &structs.VariableDecrypted{Namespace: \"default\", Path: \"app/config\", Items: map[string]string{}}\n// after\nv := &structs.VariableDecrypted{Namespace: \"default\", Path: \"app/config\", Items: map[string]string{\"key\": \"value\"}}\n// or, to remove: use the DELETE variable API, not an empty put","handlingStrategy":"validation","validationCode":"func hasItems(v structs.VariableDecrypted) bool {\n\treturn len(v.Items) > 0\n}","typeGuard":null,"tryCatchPattern":"if err := v.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"empty variables are invalid\") {\n\t\treturn fmt.Errorf(\"variable %q has no items; add key=value pairs or delete the variable instead\", v.Path)\n\t}\n\treturn err\n}","preventionTips":["Check len(Items) > 0 before put operations.","Use the delete API to remove variables rather than writing empty ones.","Fail fast in scripts when rendered item sets come out empty."],"tags":["nomad","variables","validation","empty-payload"],"backgroundTag":"empty-payload-rejected","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"}