{"record":{"id":"1d70827a5ac5492b","repo":"docker/cli","slug":"must-specify-at-least-one-container-source","errorCode":null,"errorMessage":"must specify at least one container source","messagePattern":"must specify at least one container source","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/container/cp.go","lineNumber":245,"sourceCode":"\tvar direction copyDirection\n\tif srcContainer != \"\" {\n\t\tdirection |= fromContainer\n\t\tcopyConfig.container = srcContainer\n\t}\n\tif destContainer != \"\" {\n\t\tdirection |= toContainer\n\t\tcopyConfig.container = destContainer\n\t}\n\n\tswitch direction {\n\tcase fromContainer:\n\t\treturn copyFromContainer(ctx, dockerCli, copyConfig)\n\tcase toContainer:\n\t\treturn copyToContainer(ctx, dockerCli, copyConfig)\n\tcase acrossContainers:\n\t\treturn errors.New(\"copying between containers is not supported\")\n\tdefault:\n\t\treturn errors.New(\"must specify at least one container source\")\n\t}\n}\n\nfunc resolveLocalPath(localPath string) (absPath string, _ error) {\n\tabsPath, err := filepath.Abs(localPath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil\n}\n\nfunc copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpConfig) (err error) {\n\tdstPath := copyConfig.destPath\n\tsrcPath := copyConfig.sourcePath\n\n\tif dstPath != \"-\" {\n\t\t// Get an absolute destination path.\n\t\tdstPath, err = resolveLocalPath(dstPath)","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/command/container/cp.go#L227-L263","documentation":"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).","triggerScenarios":"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`.","commonSituations":"Users treating `docker cp` like the regular cp; forgetting the CONTAINER: prefix; copy-paste of two local paths; typos dropping the container name.","solutions":["Prefix one side with the container: `docker cp ./file.txt web:/tmp` or `docker cp web:/tmp/file ./`.","If you genuinely want a local copy, use `cp`/`rsync`, not `docker cp`.","Re-check the argument order: source and destination, with at least one as CONTAINER:PATH."],"exampleFix":"# before (no container involved)\ndocker cp ./file.txt ./backup/\n# after\ndocker cp ./file.txt web:/tmp/","handlingStrategy":"validation","validationCode":"// Ensure at least one side is a container spec:\nsrcCtr, _ := splitCpArg(src)\ndstCtr, _ := splitCpArg(dst)\nif srcCtr == \"\" && dstCtr == \"\" {\n    return errors.New(\"must specify at least one container (CONTAINER:PATH) as source or destination\")\n}","typeGuard":"func involvesContainer(args ...string) bool {\n    for _, a := range args { if ctr, _ := splitCpArg(a); ctr != \"\" { return true } }\n    return false\n}","tryCatchPattern":"if err := runCopy(ctx, cli, opts); err != nil {\n    if strings.Contains(err.Error(), \"must specify at least one container source\") {\n        // guide user to add CONTAINER:PATH on one side\n    }\n    return err\n}","preventionTips":["Prefix one argument with CONTAINER: in every `docker cp`.","Use plain cp/rsync for local-to-local copies.","Validate argv shape in wrappers."],"tags":["container","cp","validation","filesystem"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}