docker/cli · error

must specify at least one container source

Error message

must specify at least one container source

What it means

runCopy switches on the computed direction. If neither source nor destination contains a container specifier (direction == 0, the default case at line 244-245), it returns errors.New("must specify at least one container source"). At least one side of a `docker cp` must be a CONTAINER:PATH; a pure local-to-local copy is not what `docker cp` does (use cp/rsync instead).

Solutions

  1. Prefix one side with the container: `docker cp ./file.txt web:/tmp` or `docker cp web:/tmp/file ./`.
  2. If you genuinely want a local copy, use `cp`/`rsync`, not `docker cp`.
  3. Re-check the argument order: source and destination, with at least one as CONTAINER:PATH.

Example fix

# before (no container involved)
docker cp ./file.txt ./backup/
# after
docker cp ./file.txt web:/tmp/
Defensive patterns

Strategy: validation

Validate before calling

// Ensure at least one side is a container spec:
srcCtr, _ := splitCpArg(src)
dstCtr, _ := splitCpArg(dst)
if srcCtr == "" && dstCtr == "" {
    return errors.New("must specify at least one container (CONTAINER:PATH) as source or destination")
}

Type guard

func involvesContainer(args ...string) bool {
    for _, a := range args { if ctr, _ := splitCpArg(a); ctr != "" { return true } }
    return false
}

Try / catch

if err := runCopy(ctx, cli, opts); err != nil {
    if strings.Contains(err.Error(), "must specify at least one container source") {
        // guide user to add CONTAINER:PATH on one side
    }
    return err
}

Prevention

When it happens

Trigger: Running `docker cp ./file.txt ./backup/` — both arguments lack the CONTAINER:PATH form, so splitCpArg returns "" for both containers and direction stays 0. Also `docker cp a.txt b.txt`.

Common situations: Users treating `docker cp` like the regular cp; forgetting the CONTAINER: prefix; copy-paste of two local paths; typos dropping the container name.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/1d70827a5ac5492b. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/container/cp.go:245

	var direction copyDirection
	if srcContainer != "" {
		direction |= fromContainer
		copyConfig.container = srcContainer
	}
	if destContainer != "" {
		direction |= toContainer
		copyConfig.container = destContainer
	}

	switch direction {
	case fromContainer:
		return copyFromContainer(ctx, dockerCli, copyConfig)
	case toContainer:
		return copyToContainer(ctx, dockerCli, copyConfig)
	case acrossContainers:
		return errors.New("copying between containers is not supported")
	default:
		return errors.New("must specify at least one container source")
	}
}

func resolveLocalPath(localPath string) (absPath string, _ error) {
	absPath, err := filepath.Abs(localPath)
	if err != nil {
		return "", err
	}
	return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
}

func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpConfig) (err error) {
	dstPath := copyConfig.destPath
	srcPath := copyConfig.sourcePath

	if dstPath != "-" {
		// Get an absolute destination path.
		dstPath, err = resolveLocalPath(dstPath)

View on GitHub (pinned to 4f84911bfe)