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
- Inspect the wrapped copyInternal error to find the real cause (permissions, disk space, timeout).
- Increase FileCopyOpts.Timeout if the transfer is timing out on large files.
- Verify read access on the source host and write access on the destination host for the given paths.
- 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
- Set a generous FileCopyOpts.Timeout for cross-host moves of large files.
- Check source read and destination write permissions before moving.
- Prefer same-host URIs when a plain rename is intended.
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
- file data size %d exceeds transfer limit of %d bytes
- error parsing destination connection: %w
- move internal, src and dest hosts do not match
- no window found with workspace ${data.workspaceid}
- beginning of file
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/36d9420533c51b0f.
Report an issue: GitHub.