hashicorp/terraform · error

Error reading script: %s

Error message

Error reading script: %s

What it means

Raised in Communicator.UploadScript when reader.Peek(2) fails while inspecting the first two bytes of the script input to detect a shebang (#!) prefix. The SSH communicator peeks at the script body before uploading so it can inject DefaultShebang (#!/bin/sh) on unix targets if no shebang is present. A failure here means the input io.Reader could not supply even two bytes, typically because it is empty or already exhausted.

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 c9def3e214)

Solutions

  1. Ensure the script content passed to the provisioner is non-empty and the io.Reader is freshly created (not previously read).
  2. If using inline scripts, verify the script block contains at least one command line.
  3. If uploading from a file, confirm the file exists and is non-empty before passing its reader.

Example fix

// before
script := bytes.NewReader(nil) // empty reader
comm.UploadScript(path, script)

// after
script := bytes.NewReader([]byte("#!/bin/sh\necho hello\n"))
comm.UploadScript(path, script)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the script reader is non-empty before calling UploadScript
func validateScriptReader(r io.Reader) error {
    data, err := io.ReadAll(r)
    if err != nil {
        return fmt.Errorf("cannot read script: %w", err)
    }
    if len(data) < 2 {
        return errors.New("script content is empty or too short")
    }
    return nil
}

Prevention

When it happens

Trigger: Calling UploadScript with a nil, empty, zero-length, or already-consumed io.Reader. This occurs when a provisioner passes a bytes.Reader that has been fully read, an empty *os.File, or a reader whose underlying stream was closed before the upload began.

Common situations: A terraform remote-exec provisioner whose inline script is empty, a file() interpolation that resolved to an empty string, or a reader being consumed earlier in the pipeline before being passed to UploadScript.

Related errors


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