wavetermdev/waveterm · error

error starting file stream for %q: %w

Error message

error starting file stream for %q: %w

What it means

RemoteFileCopyCommand in wshremote wraps the failure of the initial RemoteFileStreamCommand RPC call that opens the read stream on the source connection. This wrapper error is raised when the writer-side stream could not be started, so the copy aborts before any bytes are transferred. The underlying cause is always in the wrapped error (auth, route, or stream-open failure).

Source

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

	defer destFile.Close()

	if wshfs.RpcClientRouteId == "" {
		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() {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Inspect the wrapped cause (%w) to identify whether it is file-not-found, permission, or RPC routing.
  2. Verify the source URI exists via RemoteFileInfoCommand before copying.
  3. Check that the remote connection is alive and the writerRouteId is valid/registered.
  4. Retry the copy after re-establishing the connection.

Example fix

// before
_, err := wshclient.RemoteFileCopyCommand(ctx, copyData, nil)
// after
info, err := wshclient.RemoteFileInfoCommand(ctx, srcUri, nil)
if err != nil {
    return fmt.Errorf("source missing, aborting copy: %w", err)
}
_, err = wshclient.RemoteFileCopyCommand(ctx, copyData, nil)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := wshclient.RemoteFileInfoCommand(ctx, srcUri, nil); err != nil {
    return fmt.Errorf("source unavailable: %w", err)
}

Try / catch

err := wshclient.RemoteFileCopyCommand(ctx, data, nil)
var startErr error
if err != nil && strings.Contains(err.Error(), "error starting file stream") {
    startErr = err // wrapped cause: check connection/route, then retry
}

Prevention

When it happens

Trigger: Calling RemoteFileCopyCommand where wshclient.RemoteFileStreamCommand (routed to writerRouteId) returns an error, e.g. the source file does not exist, RPC routing fails, or the remote peer rejects the stream request.

Common situations: Copying a file whose source path was deleted between listing and copy; stale or invalid writer route id; remote connection dropped mid-copy setup; permission denied on the source file.

Related errors


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