{"record":{"id":"98788d08c63157bf","repo":"hashicorp/packer","slug":"cannot-recursively-call-templatefile-from-inside-t","errorCode":null,"errorMessage":"cannot recursively call templatefile from inside templatefile call","messagePattern":"cannot recursively call templatefile from inside templatefile call","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hcl2template/function/templatefile.go","lineNumber":98,"sourceCode":"\t\t// We'll pre-check references in the template here so we can give a\n\t\t// more specialized error message than HCL would by default, so it's\n\t\t// clearer that this problem is coming from a templatefile call.\n\t\tfor _, traversal := range expr.Variables() {\n\t\t\troot := traversal.RootName()\n\t\t\tif _, ok := ctx.Variables[root]; !ok {\n\t\t\t\treturn cty.DynamicVal, function.NewArgErrorf(1, \"vars map does not contain key %q, referenced at %s\", root, traversal[0].SourceRange())\n\t\t\t}\n\t\t}\n\n\t\tgivenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems\n\t\tfuncs := make(map[string]function.Function, len(givenFuncs))\n\t\tfor name, fn := range givenFuncs {\n\t\t\tif name == \"templatefile\" {\n\t\t\t\t// We stub this one out to prevent recursive calls.\n\t\t\t\tfuncs[name] = function.New(&function.Spec{\n\t\t\t\t\tParams: params,\n\t\t\t\t\tType: func(args []cty.Value) (cty.Type, error) {\n\t\t\t\t\t\treturn cty.NilType, fmt.Errorf(\"cannot recursively call templatefile from inside templatefile call\")\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfuncs[name] = fn\n\t\t}\n\t\tctx.Functions = funcs\n\n\t\tval, diags := expr.Value(ctx)\n\t\tif diags.HasErrors() {\n\t\t\treturn cty.DynamicVal, diags\n\t\t}\n\t\treturn val, nil\n\t}\n\n\treturn function.New(&function.Spec{\n\t\tParams: params,\n\t\tType: func(args []cty.Value) (cty.Type, error) {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/hcl2template/function/templatefile.go#L80-L116","documentation":"Inside a `templatefile()` call, the function set is augmented with a stub for `templatefile` itself, so any nested invocation immediately returns this error. Packer forbids recursion to avoid infinite template expansion.","triggerScenarios":"A template rendered via `templatefile(path, vars)` itself calls `templatefile(...)` (e.g. an include of another template), directly or transitively through a helper template.","commonSituations":"Refactoring templates into shared partials and trying to include them from a rendered template; forgetting the recursion guard when porting Terraform habits (Terraform allows templatefile nesting; Packer does not).","solutions":["Inline the nested template's content into the outer template","Pre-render the inner file separately: call templatefile on it and pass its result as a variable to the outer templatefile call","Restructure to do composition in HCL (e.g. `templatefile(\"outer.tftpl\", { inner = file(\"inner.txt\") })`) using file() instead of nested templatefile"],"exampleFix":"// before (inner.tftpl calls templatefile again)\n# outer.tftpl\n${templatefile(\"inner.tftpl\", { name = name })}\n// after\n# outer.tftpl\n${file(\"inner.tftpl\")}\n# or pre-render:\n# packer hcl: inner = templatefile(\"inner.tftpl\", { name = name }) then templatefile(\"outer.tftpl\", { inner = inner })","handlingStrategy":"validation","validationCode":"// ensure templates never call templatefile inside a .tftpl\ngrep -n 'templatefile' *.tftpl && { echo 'recursive templatefile found' >&2; exit 1; } || true","typeGuard":null,"tryCatchPattern":"// CLI check before build\nif grep -rq 'templatefile(' templates/*.tftpl; then\n  echo \"templatefile cannot be nested inside templates\" >&2\nfi","preventionTips":["Never call templatefile from inside a .tftpl file","Use file() for static includes and compose results in HCL locals","Pre-render nested templates and pass results as variables","Run `packer validate` to catch this before a build"],"tags":["hcl2","function","templatefile","recursion"],"backgroundTag":"recursive-function-call-forbidden","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}