{"record":{"id":"c4d8bac0a2ce7b97","repo":"hashicorp/nomad","slug":"missing-node-variable","errorCode":null,"errorMessage":"missing node variable","messagePattern":"missing node variable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/taskenv/env.go","lineNumber":323,"sourceCode":"\t\t}\n\t}\n\n\t// Prepare task-based secrets for use in interpolation\n\tfor k, v := range t.TaskSecrets {\n\t\tif err := addNestedKey(allMap, k, v); err != nil {\n\t\t\terrs[k] = err\n\t\t}\n\t}\n\n\t// Add flat envMap as a Map to allMap so users can access any key via\n\t// HCL2's indexing syntax: ${env[\"foo...bar\"]}\n\tallMap[\"env\"] = cty.MapVal(envMap)\n\n\t// Add meta and attr to node if they exist to properly namespace things\n\t// a bit.\n\tnodeMapI, ok := allMap[\"node\"]\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"missing node variable\")\n\t}\n\tnodeMap, ok := nodeMapI.(map[string]any)\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"invalid type for node variable: %T\", nodeMapI)\n\t}\n\tif attrMap, ok := allMap[\"attr\"]; ok {\n\t\tnodeMap[\"attr\"] = attrMap\n\t}\n\tif metaMap, ok := allMap[\"meta\"]; ok {\n\t\tnodeMap[\"meta\"] = metaMap\n\t}\n\n\t// ctyify the entire tree of strings and maps\n\ttree, err := ctyify(allMap)\n\tif err != nil {\n\t\t// This should not be possible and is likely a programming\n\t\t// error. Invalid user input should be cleaned earlier.\n\t\treturn nil, nil, err","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/taskenv/env.go#L305-L341","documentation":"TaskEnv.AllValues builds the HCL2 variable tree (env, node, attr, meta) used for interpolation. It requires a 'node' object in the assembled map; since node values normally come from t.NodeAttrs (e.g. 'node.name', 'node.datacenter'), their absence means the TaskEnv was never populated with node data, which the function treats as a fatal programming/setup error.","triggerScenarios":"AllValues() is called on a TaskEnv whose NodeAttrs map contains no keys that nest under 'node' (no node.* attributes were added via Build(), or the empty/partial TaskEnv was constructed without node information).","commonSituations":"Calling AllValues on a zero-value or manually constructed TaskEnv instead of one built by the client; tests instantiating TaskEnv with only EnvMap; hooks running before node attributes are attached (e.g. pre-restore paths).","solutions":["Build the TaskEnv via the standard client path (alloc runner's envBuilder with Node attributes) before calling AllValues","In tests/utilities, populate NodeAttrs with at least node.name/node.datacenter equivalents so a 'node' object exists","Guard the call: only invoke AllValues on TaskEnv instances that have non-empty NodeAttrs","If hit in a Nomad hook, capture the alloc/hook context and file an upstream bug — docs call this 'likely a programming error'"],"exampleFix":"// before: AllValues on a bare TaskEnv panics into error\ntenv := &taskenv.TaskEnv{}\nvars, _, err := tenv.AllValues()\n// after: build with node attrs first\ntenv := taskenv.NewEmptyBuilder().UpdateNode(node).Build()\nvars, _, err := tenv.AllValues()","handlingStrategy":"validation","validationCode":"// Go: verify the TaskEnv has node attributes before calling AllValues\nfunc hasNodeAttrs(t *taskenv.TaskEnv) bool {\n    if t == nil {\n        return false\n    }\n    for k := range t.NodeAttrs {\n        if strings.HasPrefix(k, \"node.\") {\n            return true\n        }\n    }\n    return false\n}","typeGuard":"// Go\nfunc builtTaskEnv(t *taskenv.TaskEnv) (*taskenv.TaskEnv, bool) {\n    if t == nil || len(t.NodeAttrs) == 0 {\n        return nil, false\n    }\n    return t, true\n}","tryCatchPattern":"// Go\nvars, errs, err := tenv.AllValues()\nif err != nil {\n    if strings.Contains(err.Error(), \"missing node variable\") {\n        return nil, fmt.Errorf(\"TaskEnv not built with node attributes; use the client env builder: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Always construct TaskEnv via the client's env builder, not struct literals","In tests, seed NodeAttrs with node.* keys before calling AllValues","Treat a fatal error from AllValues as a caller bug, not user input","Check hook ordering so interpolation runs after node attributes are attached"],"tags":["nomad","taskenv","interpolation","cty"],"backgroundTag":"missing-node-variable","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"}