docker/cli · error
context must be a directory
Error message
context must be a directory
What it means
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.
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.
Example fix
# before
docker plugin create myplugin ./plugin/config.json
# after
# layout: ./plugin/{config.json,rootfs/}
docker plugin create myplugin ./plugin Defensive patterns
Strategy: type-guard
Validate before calling
// Confirm the context path is a directory before invoking plugin create
info, err := os.Stat(path)
if err != nil { return err }
if !info.IsDir() {
return fmt.Errorf("%s is not a directory; expected a folder with config.json and rootfs/", path)
} Type guard
func isPluginContextDir(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir()
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: Pointing at the config.json file instead of its parent directory; passing a tar archive path; typos resolving to a regular file.
Related errors
- the plugin must be disabled before upgrading
- plugin upgrade has been cancelled
- every ip-range or gateway must have a corresponding subnet
- multiple overlapping subnet configuration is not supported
- network prune has been cancelled
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/7e261856d7c9947a.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/plugin/create.go:55
err = json.NewDecoder(dt).Decode(&m)
_ = dt.Close()
return err
}
// validateContextDir validates the given dir and returns its absolute path on success.
func validateContextDir(contextDir string) (string, error) {
absContextDir, err := filepath.Abs(contextDir)
if err != nil {
return "", err
}
stat, err := os.Lstat(absContextDir)
if err != nil {
return "", err
}
if !stat.IsDir() {
return "", errors.New("context must be a directory")
}
return absContextDir, nil
}
type pluginCreateOptions struct {
repoName string
context string
compress bool
}
func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
options := pluginCreateOptions{}
cmd := &cobra.Command{
Use: "create [OPTIONS] PLUGIN PLUGIN-DATA-DIR",
Short: "Create a plugin from a rootfs and configuration. Plugin data directory must contain config.json and rootfs directory.",
Args: cli.RequiresMinArgs(2),View on GitHub (pinned to 4f84911bfe)