hashicorp/terraform · error

Error reading error message

Error message

Error reading error message: %s

What it means

Emitted by checkSCPStatus when an SCP error indicator byte was received from the remote but the subsequent error line could not be read with r.ReadLine(). The remote signalled failure (non-zero status byte) yet the connection broke before the human-readable error message arrived, so only the wrapped read error is reported.

Solutions

  1. Re-run the apply; SCP transfers are retried within the connection timeout.
  2. Confirm scp is installed on the target and is the OpenSSH variant (busybox scp has known incompatibilities).
  3. Stabilize the bastion/proxy path; increase connection.timeout for slow links.
  4. For repeated failures, upload smaller files or use file provisioner with a local-exec alternative.
Defensive patterns

Strategy: retry

Try / catch

// In Go, SCP read errors after a fatal status byte are usually transient;
// retry the upload within the connection timeout, then fail with context:
if strings.Contains(err.Error(), "Error reading error message") {
    if attempt < maxAttempts { time.Sleep(backoff); continue }
    return fmt.Errorf("scp transfer failed and remote error was lost: %w", err)
}

Prevention

When it happens

Trigger: The SCP sub-protocol exchanged a fatal code then the underlying connection dropped or timed out before the message bytes arrived. Common with the remote scp binary crashing, a proxy/bastion severing the link, or a slow link where the read blocks past the timeout.

Common situations: Bastion/proxy instability mid-transfer; remote scp not installed or wrong variant (busybox vs OpenBSD scp) sending unexpected output; aggressive idle timeout on a load balancer.

Related errors


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

Appendix: source

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

	}

	return nil
}

// checkSCPStatus checks that a prior command sent to SCP completed
// successfully. If it did not complete successfully, an error will
// be returned.
func checkSCPStatus(r *bufio.Reader) error {
	code, err := r.ReadByte()
	if err != nil {
		return err
	}

	if code != 0 {
		// Treat any non-zero (really 1 and 2) as fatal errors
		message, _, err := r.ReadLine()
		if err != nil {
			return fmt.Errorf("Error reading error message: %s", err)
		}

		return errors.New(string(message))
	}

	return nil
}

var testUploadSizeHook func(size int64)

func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader, size int64) error {
	if testUploadSizeHook != nil {
		testUploadSizeHook(size)
	}

	if size == 0 {
		// Create a temporary file where we can copy the contents of the src
		// so that we can determine the length, since SCP is length-prefixed.

View on GitHub (pinned to d32a084675)