{"record":{"id":"7e261856d7c9947a","repo":"docker/cli","slug":"context-must-be-a-directory","errorCode":null,"errorMessage":"context must be a directory","messagePattern":"context must be a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/plugin/create.go","lineNumber":55,"sourceCode":"\terr = json.NewDecoder(dt).Decode(&m)\n\t_ = dt.Close()\n\n\treturn err\n}\n\n// validateContextDir validates the given dir and returns its absolute path on success.\nfunc validateContextDir(contextDir string) (string, error) {\n\tabsContextDir, err := filepath.Abs(contextDir)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tstat, err := os.Lstat(absContextDir)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif !stat.IsDir() {\n\t\treturn \"\", errors.New(\"context must be a directory\")\n\t}\n\n\treturn absContextDir, nil\n}\n\ntype pluginCreateOptions struct {\n\trepoName string\n\tcontext  string\n\tcompress bool\n}\n\nfunc newCreateCommand(dockerCLI command.Cli) *cobra.Command {\n\toptions := pluginCreateOptions{}\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"create [OPTIONS] PLUGIN PLUGIN-DATA-DIR\",\n\t\tShort: \"Create a plugin from a rootfs and configuration. Plugin data directory must contain config.json and rootfs directory.\",\n\t\tArgs:  cli.RequiresMinArgs(2),","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/command/plugin/create.go#L37-L73","documentation":"Thrown by validateContextDir (used by 'docker plugin create') after Lstat'ing the PLUGIN-DATA-DIR argument: if the path is not a directory (stat.IsDir() is false), the error fires. The create command requires a directory containing a config.json file and a rootfs/ subdirectory.","triggerScenarios":"Running 'docker plugin create myplugin ./config.json' (passing a file) or pointing the second argument at a symlink/device that is not a directory (lines 49-56 of create.go).","commonSituations":"Pointing at the config.json file instead of its parent directory; passing a tar archive path; typos resolving to a regular file.","solutions":["Pass the directory that contains config.json and rootfs/, not the file itself.","Create the required layout: a folder with config.json and a rootfs/ subdirectory.","Verify with 'ls -la <path>' that the target is a directory before running."],"exampleFix":"# before\ndocker plugin create myplugin ./plugin/config.json\n# after\n# layout: ./plugin/{config.json,rootfs/}\ndocker plugin create myplugin ./plugin","handlingStrategy":"type-guard","validationCode":"// Confirm the context path is a directory before invoking plugin create\ninfo, err := os.Stat(path)\nif err != nil { return err }\nif !info.IsDir() {\n    return fmt.Errorf(\"%s is not a directory; expected a folder with config.json and rootfs/\", path)\n}","typeGuard":"func isPluginContextDir(path string) bool {\n    info, err := os.Stat(path)\n    return err == nil && info.IsDir()\n}","tryCatchPattern":null,"preventionTips":["Pass the directory containing config.json and rootfs/, not the file.","Stat the path and check IsDir() in build scripts.","Validate the expected layout (config.json + rootfs/) before calling create."],"tags":["plugin","docker","go","cli","filesystem"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}