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

  1. Inline the nested template's content into the outer template
  2. Pre-render the inner file separately: call templatefile on it and pass its result as a variable to the outer templatefile call
  3. 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

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


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/98788d08c63157bf. Report an issue: GitHub.