golang/go · error

destination is not a directory

Error message

destination is not a directory

What it means

Returned by the script Cp command when more than two arguments are supplied (multiple sources to one destination) but the destination path does not resolve to an existing directory. Cp requires that a multi-source invocation copy into a directory; otherwise the copy is ambiguous. The error is wrapped as an fs.PathError with Op "cp" and the destination Path so it carries the offending path.

Source

Thrown at src/cmd/internal/script/cmds.go:292

func Cp() Cmd {
	return Command(
		CmdUsage{
			Summary: "copy files to a target file or directory",
			Args:    "src... dst",
			Detail: []string{
				"src can include 'stdout' or 'stderr' to copy from the script's stdout or stderr buffer.",
			},
		},
		func(s *State, args ...string) (WaitFunc, error) {
			if len(args) < 2 {
				return nil, ErrUsage
			}

			dst := s.Path(args[len(args)-1])
			info, err := os.Stat(dst)
			dstDir := err == nil && info.IsDir()
			if len(args) > 2 && !dstDir {
				return nil, &fs.PathError{Op: "cp", Path: dst, Err: errors.New("destination is not a directory")}
			}

			for _, arg := range args[:len(args)-1] {
				var (
					src  string
					data []byte
					mode fs.FileMode
				)
				switch arg {
				case "stdout":
					src = arg
					data = []byte(s.Stdout())
					mode = 0666
				case "stderr":
					src = arg
					data = []byte(s.Stderr())
					mode = 0666
				default:

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Precede the cp with `mkdir out/` (or the Mkdir command) so the destination directory exists.
  2. Reduce to a two-argument cp if you intend file-to-file copy: `cp a.txt b.txt`.
  3. Check the destination path spelling in the script line.
  4. If the directory exists, verify the script's working dir (script.WorkDir / cd) matches where you expect it.

Example fix

// before (script.txt)
cp a.txt b.txt out/
# -> cp out/: destination is not a directory

// after
mkdir out/
cp a.txt b.txt out/
Defensive patterns

Strategy: validation

Validate before calling

// Before cp, ensure the multi-source destination is an existing dir.
func ensureCpDest(args ...string) error {
    if len(args) <= 2 { return nil }
    info, err := os.Stat(args[len(args)-1])
    if err != nil || !info.IsDir() {
        return fmt.Errorf("%s is not a directory", args[len(args)-1])
    }
    return nil
}

Type guard

func isDestNotDir(err error) bool {
    var pe *fs.PathError
    return errors.As(err, &pe) && pe.Op == "cp" && pe.Err != nil &&
        strings.Contains(pe.Err.Error(), "destination is not a directory")
}

Try / catch

// In a script test, mkdir first; the engine surfaces the PathError directly.
mkdir out/
cp a.txt b.txt out/

Prevention

When it happens

Trigger: Writing a script-test line like `cp a.txt b.txt dir/` where 'dir/' does not exist or is a regular file. Cp is invoked with len(args) > 2, os.Stat(dst) either errors or returns a non-directory, so dstDir is false and the PathError is returned before any source is read.

Common situations: Script-test author forgets to `mkdir` the target directory first, or typos the destination name, or points at a file that should have been a directory. Also when copying stdout/stderr plus a file into a non-existent dir: `cp stdout a.txt out/`.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/65160adf0ff8d733. Report an issue: GitHub.