opentofu/opentofu · error

Upload failed: %w

Error message

Upload failed: %w

What it means

The file provisioner wraps communicator errors from uploading a directory (comm.UploadDir) in 'Upload failed: %w'. The underlying cause is typically lost connectivity, permission denial on the remote destination, an invalid destination path, or a full disk on the target.

Source

Thrown at internal/builtin/provisioners/file/resource_provisioner.go:215

	// disconnect when the context is canceled, which will close this after
	// Apply as well.
	go func() {
		<-ctx.Done()
		if err := comm.Disconnect(); err != nil {
			log.Printf("[ERROR] Unable to close provisioner connection: %s", err.Error())
		}
	}()

	info, err := os.Stat(src)
	if err != nil {
		return err
	}

	// If we're uploading a directory, short circuit and do that
	if info.IsDir() {
		if err := comm.UploadDir(dst, src); err != nil {
			return fmt.Errorf("Upload failed: %w", err)
		}
		return nil
	}

	// We're uploading a file...
	f, err := os.Open(src)
	if err != nil {
		return err
	}
	defer f.Close()

	err = comm.Upload(dst, f)
	if err != nil {
		return fmt.Errorf("Upload failed: %w", err)
	}

	return err
}

View on GitHub (pinned to 3561785c48)

Solutions

  1. Verify connectivity from the same host (manual ssh/winrm to the target) and check the connection block settings.
  2. Make destination an absolute path that exists and is writable by the login user.
  3. Check disk space and directory permissions on the target.
  4. For slow or flaky links, raise connection timeout or upload a single archive and unpack it with remote-exec instead of a directory tree.

Example fix

# before
event = "relative/dir"  # destination = "relative/dir" (ambiguous/unsupported)
provisioner "file" { source = "conf/", destination = "conf" }

# after: absolute destination path
provisioner "file" { source = "conf/", destination = "/etc/myapp/conf" }
Defensive patterns

Strategy: retry

Validate before calling

# CI pre-flight from the same host that runs apply
ssh -o BatchMode=yes user@target 'test -w /etc/myapp/conf || echo NOT_WRITABLE'

Try / catch

if err := comm.UploadDir(dst, src); err != nil {
    // transient transport failures (EOF, timeout) are retryable;
    // permission errors are not — inspect the wrapped error
    if isTransient(err) { time.Sleep(backoff); retry() }
}

Prevention

When it happens

Trigger: provisioner "file" with source pointing at a directory during apply, where the SSH/WinRM transfer of the directory tree fails mid-operation.

Common situations: Destination path not absolute or not writable by the login user; connection dropped through a bastion/NAT timeout; disk full on the target; WinRM path-style mismatches; uploading many small files over a slow link until timeout.

Related errors


AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15). Data as JSON: /api/errors/28d8c89268661bef. Report an issue: GitHub.