hashicorp/terraform · error

Failed to open script '%s': %v

Error message

Failed to open script '%s': %v

What it means

Returned by the script-reading helper in remote-exec at resource_provisioner.go:221 when os.Open(s) fails for one of the local script paths declared in scripts = [...]. The %s is the offending path and %v the OS error. Open handles are closed before returning, so this is a clean early failure before any remote activity.

Source

Thrown at internal/builtin/provisioners/remote-exec/resource_provisioner.go:221

				return nil, errors.New("invalid null string in 'script'")
			}
			s := script.AsString()
			if s == "" {
				return nil, errors.New("invalid empty string in 'script'")
			}
			scripts = append(scripts, s)
		}
	}

	// Open all the scripts
	var fhs []io.ReadCloser
	for _, s := range scripts {
		fh, err := os.Open(s)
		if err != nil {
			for _, fh := range fhs {
				fh.Close()
			}
			return nil, fmt.Errorf("Failed to open script '%s': %v", s, err)
		}
		fhs = append(fhs, fh)
	}

	// Done, return the file handles
	return fhs, nil
}

// runScripts is used to copy and execute a set of scripts
func runScripts(ctx context.Context, o provisioners.UIOutput, comm communicator.Communicator, scripts []io.ReadCloser) error {
	retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout())
	defer cancel()

	// Wait and retry until we establish the connection
	err := communicator.Retry(retryCtx, func() error {
		return comm.Connect(o)
	})
	if err != nil {

View on GitHub (pinned to c9def3e214)

Solutions

  1. Use an absolute path or verify the path resolves relative to the Terraform configuration directory.
  2. Ensure the script file exists in the repo/workspace and is readable.
  3. Check file permissions (chmod +r) for the user running Terraform.

Example fix

// before
provisioner "remote-exec" { scripts = ["scripts/setup.sh"] } // Failed to open script

// after
provisioner "remote-exec" { scripts = ["${path.module}/scripts/setup.sh"] }
Defensive patterns

Strategy: validation

Validate before calling

// Validate every script path in the provisioner config before running.
for _, s := range scripts {
    if _, err := os.Stat(s); err != nil {
        return fmt.Errorf("script %q missing: %w", s, err)
    }
}

Prevention

When it happens

Trigger: A remote-exec provisioner with scripts = ["./run.sh"] where the file does not exist, is unreadable, or the path is wrong relative to the Terraform working directory.

Common situations: Relative path resolved against the wrong cwd; script file not committed to the repo; file permissions deny the Terraform process; typo in the path.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/2fe97fdf3c0a0b7c. Report an issue: GitHub.