hashicorp/packer · error

Only one of script or scripts can be specified.

Error message

Only one of script or scripts can be specified.

What it means

This is a template-validation error thrown by the shell provisioner's Prepare() phase in Packer. The provisioner accepts two mutually exclusive ways to supply a script: the singular `script` field and the plural `scripts` list. If both are set in the same provisioner block, Prepare refuses to guess which one to use and returns this error before any build runs.

Source

Thrown at provisioner/shell/provisioner.go:152

		p.config.RemoteFile = fmt.Sprintf("script_%d.sh", rand.Intn(9999))
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = fmt.Sprintf("%s/%s", p.config.RemoteFolder, p.config.RemoteFile)
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	if p.config.Vars == nil {
		p.config.Vars = make([]string, 0)
	}

	var errs *packersdk.MultiError
	if p.config.Script != "" && len(p.config.Scripts) > 0 {
		errs = packersdk.MultiErrorAppend(errs,
			errors.New("Only one of script or scripts can be specified."))
	}

	if p.config.Script != "" {
		p.config.Scripts = []string{p.config.Script}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = packersdk.MultiErrorAppend(errs,
			errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = packersdk.MultiErrorAppend(errs,
			errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = packersdk.MultiErrorAppend(errs,
				fmt.Errorf("Bad script '%s': %s", path, err))

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Remove either the `script` or the `scripts` key from the provisioner block so only one remains.
  2. If you want multiple scripts, delete `script` and list every file in `scripts`.
  3. If you only need one script, keep `script` and delete the `scripts` list.

Example fix

// before
provisioner "shell" {
  script  = "./setup.sh"
  scripts = ["./a.sh", "./b.sh"]
}
// after
provisioner "shell" {
  scripts = ["./a.sh", "./b.sh"]
}
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Script != "" && len(cfg.Scripts) > 0 {
    return fmt.Errorf("provisioner config: set only one of script or scripts")
}

Prevention

When it happens

Trigger: Calling Prepare() with a config where p.config.Script is a non-empty string AND p.config.Scripts has length > 0 (e.g. HCL2/JSON template sets both `script = "a.sh"` and `scripts = ["b.sh"]` in the same shell provisioner block).

Common situations: Users migrating an old single-script template to a multi-script layout and leaving the old `script` key behind; copy-pasting a provisioner block that already had `scripts` and adding `script` for a quick test; SDK/plugin code programmatically populating both fields from overlapping template sections.

Related errors


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