hashicorp/packer · error

Error interpolating source

Error message

Error interpolating source: %s

What it means

In ProvisionDownload, each source path is interpolated with the provisioner's template context. A failure (unknown variable, bad syntax, missing generated data) aborts the whole download loop with this error. The underlying renderer error is embedded after the colon.

Solutions

  1. Correct the variable name or syntax in the source path
  2. Use only generated-data keys documented for the builder
  3. Simplify the source path to a literal string if no substitution is needed
  4. Run packer validate / inspect the wrapped error for the exact failing key

Example fix

// before
source = "{{ .BuildName }}/{{ .Nonexistent }}.log"
// after
source = "{{ build_name }}.log"
Defensive patterns

Strategy: try-catch

Validate before calling

// Test source templates against the builder's generated data before building:
// packer validate template.pkr.hcl

Try / catch

if err := provision(ctx, ui, comm, generated); err != nil {
    if strings.Contains(err.Error(), "Error interpolating source") {
        // fix the offending source template variable
    }
}

Prevention

When it happens

Trigger: direction=download and a source entry contains template placeholders that fail to render, e.g. {{ .HostVars.x }} not exposed by the builder or malformed {{ syntax.

Common situations: Download sources using builder-specific generated data not available (e.g. {{ .Flavor }} only on some builders); typos in variable names; using provisioning-state functions not valid in this context.

Related errors


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

Appendix: source

Thrown at provisioner/file/provisioner.go:168

	}

	if p.config.Direction == "download" {
		return p.ProvisionDownload(ui, comm)
	} else {
		return p.ProvisionUpload(ui, comm)
	}
}

func (p *Provisioner) ProvisionDownload(ui packersdk.Ui, comm packersdk.Communicator) error {
	dst, err := interpolate.Render(p.config.Destination, &p.config.ctx)
	if err != nil {
		return fmt.Errorf("Error interpolating destination: %s", err)
	}
	for _, src := range p.config.Sources {
		dst := dst
		src, err := interpolate.Render(src, &p.config.ctx)
		if err != nil {
			return fmt.Errorf("Error interpolating source: %s", err)
		}

		// ensure destination dir exists.  p.config.Destination may either be a file or a dir.
		dir := dst
		// if it doesn't end with a /, set dir as the parent dir
		if !strings.HasSuffix(dst, "/") {
			dir = filepath.Dir(dir)
		} else if !strings.HasSuffix(src, "/") && !strings.HasSuffix(src, "*") {
			dst = filepath.Join(dst, filepath.Base(src))
		}
		ui.Say(fmt.Sprintf("Downloading %s => %s", src, dst))

		if dir != "" {
			err := os.MkdirAll(dir, os.FileMode(0755))
			if err != nil {
				return err
			}
		}

View on GitHub (pinned to eb36e3c3e4)