hashicorp/terraform · error
SCP failed to start. This usually means that SCP is not prop
Error message
SCP failed to start. This usually means that SCP is not properly installed on the remote system.
What it means
Returned by Communicator.scpSession (communicator.go:613). After session.Wait() the SSH command returns an *ssh.ExitError; if ExitStatus()==127 (command not found) Terraform maps it to this message. Exit 127 from the shell means the `scp` binary is not installed or not on PATH on the REMOTE system, so file upload/download via SCP cannot proceed.
Source
Thrown at internal/communicator/ssh/communicator.go:613
log.Println("[DEBUG] Waiting for SSH session to complete.")
err = session.Wait()
// log any stderr before exiting on an error
scpErr := stderr.String()
if len(scpErr) > 0 {
log.Printf("[ERROR] scp stderr: %q", stderr)
}
if err != nil {
if exitErr, ok := err.(*ssh.ExitError); ok {
// Otherwise, we have an ExitErorr, meaning we can just read
// the exit status
log.Printf("[ERROR] %s", exitErr)
// If we exited with status 127, it means SCP isn't available.
// Return a more descriptive error for that.
if exitErr.ExitStatus() == 127 {
return errors.New(
"SCP failed to start. This usually means that SCP is not\n" +
"properly installed on the remote system.")
}
}
return err
}
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 errView on GitHub (pinned to c9def3e214)
Solutions
- Install an SCP client on the remote: `apk add openssh-client` (Alpine), `apt-get install openssh-client` (Debian/Ubuntu), or the equivalent for the OS.
- Switch the file transfer to a method the remote supports: use `provisioner "remote-exec"` with curl/wget to fetch the file, or bake the file into the image.
- Verify scp is on the remote PATH for the login shell (non-interactive shells sometimes have a minimal PATH).
Example fix
# before: Alpine target has no scp -> 'SCP failed to start'
provisioner "file" { source = "app.sh" destination = "/tmp/app.sh" }
# after: install scp first, then transfer
provisioner "remote-exec" {
inline = ["apk add --no-cache openssh-client"]
}
provisioner "file" { source = "app.sh" destination = "/tmp/app.sh" } Defensive patterns
Strategy: validation
Validate before calling
// Before relying on the file provisioner, ensure scp exists on the remote.
// (run a remote-exec check, or use an image that includes openssh-client)
out, _ := sshExec(host, "command -v scp")
if strings.TrimSpace(out) == "" {
return errors.New("scp missing on remote; install openssh-client")
} Try / catch
err := communicator.Upload(dst, src)
if err != nil && strings.Contains(err.Error(), "SCP failed to start") {
// install scp on the remote, then retry the upload
} Prevention
- Bake openssh-client into the target image (Alpine: apk add openssh-client).
- For scp-less targets, fetch files via curl/wget in remote-exec instead of the file provisioner.
When it happens
Trigger: Using the `file` provisioner (or any SCP-based transfer) with connection type "ssh" against a remote host that lacks the `scp` binary — minimal/container images (Alpine, distroless), embedded devices, or locked-down bastions without openssh-client.
Common situations: Target is an Alpine container without `openssh-client` installed; a network appliance with no scp; PATH on the remote does not include /usr/bin; Windows target where scp is not in the default PATH.
Related errors
- ssh client is not connected
- connection type '%s' not supported
- SSH authentication failed (%s@%s): %w
- Failed to parse ssh private key: %s
- Cannot set both 'source' and 'content'
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/2815bc910fc208b8.
Report an issue: GitHub.