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
- Copy in two steps via the local filesystem: `docker cp web1:/path ./tmp && docker cp ./tmp web2:/path`.
- Or use a tar pipe: `docker cp web1:/path - | docker cp - web2:/path`.
- Use `docker exec` with tar/cp inside a shared volume if both containers share a volume mount.
- 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
- Remember `docker cp` always has the local filesystem as one endpoint.
- For container-to-container, pipe through a tar: `docker cp a:/x - | docker cp - b:/x`.
- Use a shared volume for frequent cross-container file exchange.
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
- source can not be empty
- destination can not be empty
- must specify at least one container source
- container ID file found, make sure the other container…
- got a device
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)