wavetermdev/waveterm · error

%s: is a directory

Error message

%s: is a directory

What it means

checkFileSize rejects directory sources with '%s: is a directory' — the cp/mv size pre-check only supports regular files, since it transfers file contents under the size cap. If the resolved source path is a directory (info.IsDir), the command fails before any transfer.

Source

Thrown at cmd/wsh/cmd/wshcmd-file.go:339

	return nil
}

func checkFileSize(path string, maxSize int64) (*wshrpc.FileInfo, error) {
	fileData := wshrpc.FileData{
		Info: &wshrpc.FileInfo{
			Path: path}}

	info, err := wshclient.FileInfoCommand(RpcClient, fileData, &wshrpc.RpcOpts{Timeout: fileTimeout})
	err = convertNotFoundErr(err)
	if err != nil {
		return nil, fmt.Errorf("getting file info: %w", err)
	}
	if info.NotFound {
		return nil, fmt.Errorf("%s: no such file", path)
	}
	if info.IsDir {
		return nil, fmt.Errorf("%s: is a directory", path)
	}
	if info.Size > maxSize {
		return nil, fmt.Errorf("file size (%d bytes) exceeds maximum of %d bytes", info.Size, maxSize)
	}
	return info, nil
}

func fileCpRun(cmd *cobra.Command, args []string) error {
	src, dst := args[0], args[1]
	merge, err := cmd.Flags().GetBool("merge")
	if err != nil {
		return err
	}
	force, err := cmd.Flags().GetBool("force")
	if err != nil {
		return err
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass the individual files inside the directory instead of the directory itself
  2. Archive the directory first (tar/zip) and copy the archive as a single file
  3. Check the path with 'wsh file info' to confirm it is a regular file before cp/mv

Example fix

// before
wsh file cp [block] ./mydir @            # error: mydir is a directory
// after
tar czf mydir.tgz mydir && wsh file cp [block] ./mydir.tgz @
Defensive patterns

Strategy: type-guard

Validate before calling

info, err := wshclient.FileInfoCommand(RpcClient, fileData, &wshrpc.RpcOpts{Timeout: fileTimeout})
if err == nil && info.IsDir {
    return fmt.Errorf("%s is a directory; cp/mv only supports regular files", path)
}

Type guard

func isRegularFile(info *wshrpc.FileInfo) bool {
    return info != nil && !info.IsDir && !info.NotFound
}

Prevention

When it happens

Trigger: 'wsh file cp <dir> <dst>' or 'wsh file mv <dir> <dst>' where the source path resolves to a directory on the remote (e.g. a directory named like the intended file, or a trailing-slash/wildcard resolving to a dir).

Common situations: Trying to cp/mv a whole directory with a command that only handles files; glob or tab-completion resolving to the directory instead of the file; assuming recursive copy is supported.

Related errors


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