hashicorp/packer · error
cannot recursively call templatefile from inside templatefil
Error message
cannot recursively call templatefile from inside templatefile call
What it means
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.
Source
Thrown at hcl2template/function/templatefile.go:98
// We'll pre-check references in the template here so we can give a
// more specialized error message than HCL would by default, so it's
// clearer that this problem is coming from a templatefile call.
for _, traversal := range expr.Variables() {
root := traversal.RootName()
if _, ok := ctx.Variables[root]; !ok {
return cty.DynamicVal, function.NewArgErrorf(1, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange())
}
}
givenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems
funcs := make(map[string]function.Function, len(givenFuncs))
for name, fn := range givenFuncs {
if name == "templatefile" {
// We stub this one out to prevent recursive calls.
funcs[name] = function.New(&function.Spec{
Params: params,
Type: func(args []cty.Value) (cty.Type, error) {
return cty.NilType, fmt.Errorf("cannot recursively call templatefile from inside templatefile call")
},
})
continue
}
funcs[name] = fn
}
ctx.Functions = funcs
val, diags := expr.Value(ctx)
if diags.HasErrors() {
return cty.DynamicVal, diags
}
return val, nil
}
return function.New(&function.Spec{
Params: params,
Type: func(args []cty.Value) (cty.Type, error) {View on GitHub (pinned to eb36e3c3e4)
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
Example fix
// before (inner.tftpl calls templatefile again)
# outer.tftpl
${templatefile("inner.tftpl", { name = name })}
// after
# outer.tftpl
${file("inner.tftpl")}
# or pre-render:
# packer hcl: inner = templatefile("inner.tftpl", { name = name }) then templatefile("outer.tftpl", { inner = inner }) Defensive patterns
Strategy: validation
Validate before calling
// ensure templates never call templatefile inside a .tftpl
grep -n 'templatefile' *.tftpl && { echo 'recursive templatefile found' >&2; exit 1; } || true Try / catch
// CLI check before build
if grep -rq 'templatefile(' templates/*.tftpl; then
echo "templatefile cannot be nested inside templates" >&2
fi Prevention
- 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
When it happens
Trigger: A template rendered via `templatefile(path, vars)` itself calls `templatefile(...)` (e.g. an include of another template), directly or transitively through a helper template.
Common situations: 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).
Related errors
- too many values, 1 needed: %v
- failed to write gzip raw data: %w
- failed to flush gzip writer: %w
- failed to close gzip writer: %w
- failed to read file %q: %s
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/98788d08c63157bf.
Report an issue: GitHub.