docker/cli · error

copying between containers is not supported

Error message

copying between containers is not supported

What it means

runCopy (cli/command/container/cp.go:215) splits source and destination into container+path via splitCpArg and ORs their directions. If both source and destination contain a container specifier (direction == acrossContainers, i.e. fromContainer|toContainer), line 242-243 returns errors.New("copying between containers is not supported"). The `docker cp` operation always involves the local filesystem as one endpoint; container-to-container copy requires two hops.

Solutions

  1. Copy in two steps via the local filesystem: `docker cp web1:/path ./tmp && docker cp ./tmp web2:/path`.
  2. Or use a tar pipe: `docker cp web1:/path - | docker cp - web2:/path`.
  3. Use `docker exec` with tar/cp inside a shared volume if both containers share a volume mount.
  4. Remember `docker cp` always has the local FS as one endpoint.

Example fix

# before (unsupported)
docker cp web1:/app/out web2:/app/in
# after — two hops through the local filesystem
docker cp web1:/app/out ./out && docker cp ./out web2:/app/in
Defensive patterns

Strategy: validation

Validate before calling

// Detect container specifiers on both sides before calling runCopy:
srcCtr, _ := splitCpArg(src)
dstCtr, _ := splitCpArg(dst)
if srcCtr != "" && dstCtr != "" {
    return errors.New("copying between containers is not supported; copy via the local filesystem")
}

Type guard

// splitCpArg returns a non-empty container token when the arg is CONTAINER:PATH.
func isContainerSpec(arg string) bool { ctr, _ := splitCpArg(arg); return ctr != "" }

Try / catch

if err := runCopy(ctx, cli, opts); err != nil {
    if strings.Contains(err.Error(), "copying between containers is not supported") {
        // fall back to a two-hop copy through the local FS
    }
    return err
}

Prevention

When it happens

Trigger: Running `docker cp web1:/path web2:/path` or `docker cp ctrA:/a ctrB:/b` — both arguments are parsed as CONTAINER:PATH because both contain a ':' with a non-relative prefix.

Common situations: Users expecting scp-like host-to-host semantics; copy-pasting container IDs into both sides; misreading docs.

Related errors


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

Appendix: source

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

	}

	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 != "-" {

View on GitHub (pinned to 4f84911bfe)