hashicorp/terraform · error

Error reading script

Error message

Error reading script: %s

What it means

Raised in UploadScript when bufio.Reader.Peek(2) fails to read the first two bytes of the script content. Peek fails when the underlying reader returns an error before any data (e.g. an empty/closed reader, a pipe whose writer end closed, or an upstream provisioner producing an I/O error). The shebang check needs those bytes to decide whether to prepend DefaultShebang.

Solutions

  1. Verify the script source is readable and non-empty before the provisioner runs (cat the file locally, or print the variable).
  2. For file-based scripts, confirm the path resolves under the module (use path.module / path.root).
  3. confirm `templatefile()` inputs exist and the template itself is valid.
  4. If using a data source for the script, ensure it returns content without error.

Example fix

// before
provisioner "remote-exec" {
  inline = file("${path.module}/scripts/setup.sh") // file missing
}

// after
provisioner "remote-exec" {
  inline = file("${path.module}/files/setup.sh") // correct path
}
Defensive patterns

Strategy: validation

Validate before calling

# Confirm the script source is readable and non-empty before apply:
#   test -s scripts/setup.sh && echo ok || echo 'missing or empty'
# In HCL, guard file() with a check variable if needed:
#   variable "script_path" { type = string; validation {...} }

Try / catch

// In Go feeding the script reader, peek ahead and fail with a clearer msg:
head := make([]byte, 2)
n, err := io.ReadFull(reader, head)
if err != nil && n == 0 {
    return fmt.Errorf("script source produced no data; check path/template: %w", err)
}

Prevention

When it happens

Trigger: An inline script passed via a broken variable reference, a file() call on a missing path, a template that evaluated to nothing, or a producer that errored/closed before yielding data. Most commonly the script source is malformed or empty beyond what Peek tolerates.

Common situations: file("${path.module}/script.sh") pointing at a non-existent file; templatefile producing an I/O error; a custom data source returning an error value used directly as the script; an empty inline script set with an expression that resolved to no bytes.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/3fbce8ad65611280. Report an issue: GitHub.

Appendix: source

Thrown at internal/communicator/ssh/communicator.go:451

	}

	scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error {
		return scpUploadFile(targetFile, input, w, stdoutR, size)
	}

	cmd, err := quoteScpCommand([]string{"scp", "-vt", targetDir}, c.connInfo.TargetPlatform)
	if err != nil {
		return err
	}
	return c.scpSession(cmd, scpFunc)
}

// UploadScript implementation of communicator.Communicator interface
func (c *Communicator) UploadScript(path string, input io.Reader) error {
	reader := bufio.NewReader(input)
	prefix, err := reader.Peek(2)
	if err != nil {
		return fmt.Errorf("Error reading script: %s", err)
	}
	var script bytes.Buffer

	if string(prefix) != "#!" && c.connInfo.TargetPlatform != TargetPlatformWindows {
		script.WriteString(DefaultShebang)
	}
	script.ReadFrom(reader)

	if err := c.Upload(path, &script); err != nil {
		return err
	}
	if c.connInfo.TargetPlatform != TargetPlatformWindows {
		var stdout, stderr bytes.Buffer
		cmd := &remote.Cmd{
			Command: fmt.Sprintf("chmod 0777 %s", path),
			Stdout:  &stdout,
			Stderr:  &stderr,
		}

View on GitHub (pinned to d32a084675)