wavetermdev/waveterm · error

cannot get info for source file %q: %w

Error message

cannot get info for source file %q: %w

What it means

RemoteFileCopyCommand first issues a RemoteFileInfoCommand RPC to the source host to stat the file being copied. This error wraps any failure of that RPC — the file may not exist, be unreadable, the connection may be down, or the RPC may time out. The wrapper adds the source URI (%q) so the caller knows which path failed; the underlying cause is in the wrapped error.

Source

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

	if srcConn.Host == destConn.Host {
		srcPathCleaned := filepath.Clean(wavebase.ExpandHomeDirSafe(srcConn.Path))
		err := remoteCopyFileInternal(data.SrcUri, data.DestUri, srcPathCleaned, destPathCleaned, destHasSlash, opts.Overwrite)
		return false, err
	}

	// FROM external TO here - only supports single file copying
	timeout := wshfs.DefaultTimeout
	if opts.Timeout > 0 {
		timeout = time.Duration(opts.Timeout) * time.Millisecond
	}
	readCtx, timeoutCancel := context.WithTimeoutCause(ctx, timeout, fmt.Errorf("timeout copying file %q to %q", data.SrcUri, data.DestUri))
	defer timeoutCancel()
	copyStart := time.Now()

	srcFileInfo, err := wshclient.RemoteFileInfoCommand(wshfs.RpcClient, srcConn.Path, &wshrpc.RpcOpts{Timeout: opts.Timeout, Route: wshutil.MakeConnectionRouteId(srcConn.Host)})
	if err != nil {
		return false, fmt.Errorf("cannot get info for source file %q: %w", data.SrcUri, err)
	}
	if srcFileInfo.IsDir {
		return false, fmt.Errorf("copying directories is not supported")
	}
	if srcFileInfo.Size > RemoteFileTransferSizeLimit {
		return false, fmt.Errorf("file %q size %d exceeds transfer limit of %d bytes", data.SrcUri, srcFileInfo.Size, RemoteFileTransferSizeLimit)
	}

	destFilePath, err := prepareDestForCopy(destPathCleaned, fspath.Base(srcConn.Path), destHasSlash, opts.Overwrite)
	if err != nil {
		return false, err
	}

	destFile, err := os.OpenFile(destFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, srcFileInfo.Mode)
	if err != nil {
		return false, fmt.Errorf("cannot create destination file %q: %w", destFilePath, err)
	}
	defer destFile.Close()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify the source URI exists and is readable on the source host (run `wsh ls` or an equivalent RemoteFileInfoCommand call first).
  2. Check the connection to the source host is up and routed (MakeConnectionRouteId(srcConn.Host) resolves to a live wsh client).
  3. Inspect the wrapped cause (%w) to distinguish os.ErrNotExist / permission errors from timeouts and fix accordingly.
  4. Increase opts.Timeout if large/slow hosts cause RPC timeouts.

Example fix

// before
ok, err := RemoteFileCopyCommand(ctx, wshrpc.CommandFileCopyData{SrcUri: "wsh://host/typo/path.txt", DestUri: "/tmp/out"})
// after
if _, err := wshclient.RemoteFileInfoCommand(rpc, path, &wshrpc.RpcOpts{Route: wshutil.MakeConnectionRouteId(host)}); err != nil {
    log.Fatalf("source file missing: %v", err)
}
ok, err := RemoteFileCopyCommand(ctx, wshrpc.CommandFileCopyData{SrcUri: "wsh://host/correct/path.txt", DestUri: "/tmp/out"})
Defensive patterns

Strategy: validation

Validate before calling

info, err := wshclient.RemoteFileInfoCommand(rpc, srcPath, &wshrpc.RpcOpts{Route: wshutil.MakeConnectionRouteId(host), Timeout: 10000})
if err != nil {
    return fmt.Errorf("source %q unreachable: %w", srcUri, err)
}

Type guard

func sourceFileAvailable(err error) bool { return err == nil }

Try / catch

ok, err := RemoteFileCopyCommand(ctx, data)
if err != nil {
    if strings.Contains(err.Error(), "cannot get info for source file") {
        var pathErr *fs.PathError
        if errors.As(err, &pathErr) && errors.Is(pathErr, fs.ErrNotExist) {
            // handle missing source
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling wshclient.RemoteFileInfoCommand for srcConn.Path over route MakeConnectionRouteId(srcConn.Host) returns an error: nonexistent source path, permission denied, source connection offline/not routed, or RPC timeout (opts.Timeout or wshfs.DefaultTimeout elapsed).

Common situations: Typo in the source URI; wsh not installed/running on the remote host; SSH connection dropped; copying from a mount (s3:/, wsh://) whose connection is not authenticated; source file deleted between listing and copy.

Related errors


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