wavetermdev/waveterm · error

cannot copy %q to %q: %w

Error message

cannot copy %q to %q: %w

What it means

wshfs.Move wraps copyInternal failures with 'cannot copy %q to %q'. When Move is asked to move a file across two DIFFERENT hosts, the library cannot do a remote rename, so it falls back to copy-then-delete; this error means the copy half failed and the source was left intact (the delete step never ran). The wrapped error from copyInternal identifies the actual cause (permissions, missing file, timeout, etc.).

Source

Thrown at pkg/remote/fileshare/wshfs/wshfs.go:233

func Move(ctx context.Context, data wshrpc.CommandFileCopyData) error {
	opts := data.Opts
	if opts == nil {
		opts = &wshrpc.FileCopyOpts{}
	}
	log.Printf("Move: srcuri: %v, desturi: %v, opts: %v", data.SrcUri, data.DestUri, opts)
	srcConn, err := parseConnection(ctx, data.SrcUri)
	if err != nil {
		return fmt.Errorf("error parsing source connection: %w", err)
	}
	destConn, err := parseConnection(ctx, data.DestUri)
	if err != nil {
		return fmt.Errorf("error parsing destination connection: %w", err)
	}
	if srcConn.Host != destConn.Host {
		isDir, err := copyInternal(srcConn, destConn, opts)
		if err != nil {
			return fmt.Errorf("cannot copy %q to %q: %w", data.SrcUri, data.DestUri, err)
		}
		return delete_(srcConn, opts.Recursive && isDir)
	}
	return moveInternal(srcConn, destConn, opts)
}

func Copy(ctx context.Context, data wshrpc.CommandFileCopyData) error {
	opts := data.Opts
	if opts == nil {
		opts = &wshrpc.FileCopyOpts{}
	}
	log.Printf("Copy: srcuri: %v, desturi: %v, opts: %v", data.SrcUri, data.DestUri, opts)
	srcConn, err := parseConnection(ctx, data.SrcUri)
	if err != nil {
		return fmt.Errorf("error parsing source connection: %w", err)
	}
	destConn, err := parseConnection(ctx, data.DestUri)
	if err != nil {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Inspect the wrapped copyInternal error to find the real cause (permissions, disk space, timeout).
  2. Increase FileCopyOpts.Timeout if the transfer is timing out on large files.
  3. Verify read access on the source host and write access on the destination host for the given paths.
  4. If cross-host move is not intended, ensure both URIs use the same connection host so Move uses the plain remote rename path (moveInternal).

Example fix

// before
Move(ctx, wshrpc.CommandFileCopyData{SrcUri: "conn:local:///a", DestUri: "conn:myserver://b"}) // no timeout
// after
Move(ctx, wshrpc.CommandFileCopyData{SrcUri: "conn:local:///a", DestUri: "conn:myserver://b", Opts: &wshrpc.FileCopyOpts{Timeout: 60000}})
Defensive patterns

Strategy: try-catch

Validate before calling

src, _ := connparse.ParseConnection(ctx, data.SrcUri)
dst, _ := connparse.ParseConnection(ctx, data.DestUri)
if src.Host != dst.Host { /* cross-host: expect copy fallback, set generous timeout */ }

Try / catch

if err := wshfs.Move(ctx, data); err != nil {
    if strings.Contains(err.Error(), "cannot copy") {
        // unwrap copyInternal cause, retry with larger timeout or fix permissions
    }
}

Prevention

When it happens

Trigger: Calling wshfs.Move (via FileMoveCommand) where srcConn.Host != destConn.Host and the underlying read/write transfer (copyInternal) fails — e.g. read permission denied on source, write failure on destination, or RPC timeout during the streaming copy.

Common situations: Dragging a file from a local block to an SSH remote in Wave when the remote filesystem is read-only or full; moving between two different remote connections; large files exceeding copy timeouts (opts.Timeout too small).

Related errors


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