wavetermdev/waveterm · error

error copying file %q to %q: %w

Error message

error copying file %q to %q: %w

What it means

RemoteFileCopyCommand wraps any error returned by io.Copy(destFile, reader), which transfers bytes from the remote read stream into the destination file. This means the stream started successfully but data transfer failed mid-copy (network interruption, read/write I/O error).

Source

Thrown at pkg/wshrpc/wshremote/wshremote_file.go:190

		return false, fmt.Errorf("stream broker route id not available for file copy")
	}
	writerRouteId := wshutil.MakeConnectionRouteId(srcConn.Host)
	reader, streamMeta := wshfs.RpcClient.StreamBroker.CreateStreamReader(wshfs.RpcClientRouteId, writerRouteId, 256*1024)
	log.Printf("RemoteFileCopyCommand: readroute=%s writeroute=%s", streamMeta.ReaderRouteId, streamMeta.WriterRouteId)
	defer reader.Close()
	go func() {
		<-readCtx.Done()
		reader.Close()
	}()
	streamData := wshrpc.CommandRemoteFileStreamData{
		Path:       srcConn.Path,
		StreamMeta: *streamMeta,
	}
	if _, err = wshclient.RemoteFileStreamCommand(wshfs.RpcClient, streamData, &wshrpc.RpcOpts{Route: writerRouteId}); err != nil {
		return false, fmt.Errorf("error starting file stream for %q: %w", data.SrcUri, err)
	}
	if _, err = io.Copy(destFile, reader); err != nil {
		return false, fmt.Errorf("error copying file %q to %q: %w", data.SrcUri, data.DestUri, err)
	}

	totalTime := time.Since(copyStart).Seconds()
	totalMegaBytes := float64(srcFileInfo.Size) / 1024 / 1024
	rate := float64(0)
	if totalTime > 0 {
		rate = totalMegaBytes / totalTime
	}
	log.Printf("RemoteFileCopyCommand: done; 1 file copied in %.3fs, total of %.4f MB, %.2f MB/s\n", totalTime, totalMegaBytes, rate)
	return false, nil
}

func (impl *ServerImpl) RemoteListEntriesCommand(ctx context.Context, data wshrpc.CommandRemoteListEntriesData) chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteListEntriesRtnData] {
	ch := make(chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteListEntriesRtnData], 16)
	go func() {
		defer func() {
			panichandler.PanicHandler("RemoteListEntriesCommand", recover())
		}()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check the wrapped error for network vs. local I/O causes.
  2. Verify destination disk space and write permissions.
  3. Re-run the copy; consider deleting a partial destination file first.
  4. For large/unstable transfers, ensure the connection is stable or use a resumable approach.

Example fix

// before
_, err := wshclient.RemoteFileCopyCommand(ctx, copyData, nil)
// after
err := wshclient.RemoteFileCopyCommand(ctx, copyData, nil)
if err != nil && strings.Contains(err.Error(), "error copying file") {
    _ = os.Remove(destPath) // clean partial file
    err = wshclient.RemoteFileCopyCommand(ctx, copyData, nil)
}
Defensive patterns

Strategy: retry

Validate before calling

info, err := wshclient.RemoteFileInfoCommand(ctx, srcUri, nil)
if err != nil { return err }
// check dest has enough space if size known
_ = info.Size

Try / catch

for attempt := 0; attempt < 3; attempt++ {
    err := wshclient.RemoteFileCopyCommand(ctx, data, nil)
    if err == nil { break }
    if strings.Contains(err.Error(), "error copying file") {
        _ = os.Remove(destPath); time.Sleep(backoff(attempt)); continue
    }
    return err
}

Prevention

When it happens

Trigger: io.Copy fails during RemoteFileCopyCommand: the read side (remote stream) errors out, the destination write fails (disk full, permission), or the RPC stream breaks while transferring.

Common situations: Large file transfer over a flaky SSH/network connection; destination disk quota exceeded; destination directory became unwritable after the copy began.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/b7f4bb41e5f1a57e. Report an issue: GitHub.