{"record":{"id":"760f6e07051350de","repo":"docker/cli","slug":"plugin-candidate-q-does-not-have-q-prefix","errorCode":null,"errorMessage":"plugin candidate %q: does not have %q prefix","messagePattern":"plugin candidate %q: does not have %q prefix","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli-plugins/manager/plugin.go","lineNumber":78,"sourceCode":"// non-recoverable error.\nfunc newPlugin(c pluginCandidate, cmds []*cobra.Command) (Plugin, error) {\n\tpath := c.Path()\n\tif path == \"\" {\n\t\treturn Plugin{}, errors.New(\"plugin candidate path cannot be empty\")\n\t}\n\n\t// The candidate listing process should have skipped anything\n\t// which would fail here, so there are all real errors.\n\tfullname := filepath.Base(path)\n\tif fullname == \".\" {\n\t\treturn Plugin{}, fmt.Errorf(\"unable to determine basename of plugin candidate %q\", path)\n\t}\n\tvar err error\n\tif fullname, err = trimExeSuffix(fullname); err != nil {\n\t\treturn Plugin{}, fmt.Errorf(\"plugin candidate %q: %w\", path, err)\n\t}\n\tif !strings.HasPrefix(fullname, metadata.NamePrefix) {\n\t\treturn Plugin{}, fmt.Errorf(\"plugin candidate %q: does not have %q prefix\", path, metadata.NamePrefix)\n\t}\n\n\tp := Plugin{\n\t\tName: strings.TrimPrefix(fullname, metadata.NamePrefix),\n\t\tPath: path,\n\t}\n\n\t// Now apply the candidate tests, so these update p.Err.\n\tif !isValidPluginName(p.Name) {\n\t\tp.Err = newPluginError(\"plugin candidate %q did not match %q\", p.Name, pluginNameFormat)\n\t\treturn p, nil\n\t}\n\n\tfor _, cmd := range cmds {\n\t\t// Ignore conflicts with commands which are\n\t\t// just plugin stubs (i.e. from a previous\n\t\t// call to AddPluginCommandStubs).\n\t\tif IsPluginCommand(cmd) {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli-plugins/manager/plugin.go#L60-L96","documentation":"Returned by newPlugin when the trimmed filename does not start with the docker- prefix (metadata.NamePrefix). Docker CLI plugins must be named docker-<name> so the manager can derive the subcommand name by stripping the prefix. A binary without this prefix is rejected outright.","triggerScenarios":"Placing an executable named 'compose' (instead of 'docker-compose') in the plugin search PATH, or any non-prefixed binary encountered as a candidate.","commonSituations":"Installing a plugin under its bare name, or renaming a plugin binary and dropping the docker- prefix.","solutions":["Rename the plugin executable to docker-<name>, e.g. docker-compose.","Symlink the bare name to a docker- prefixed path if the original cannot be renamed.","Remove non-prefixed binaries from the plugin search directories."],"exampleFix":"# before\nmv bin/compose ~/.docker/cli-plugins/compose\n# after\nmv bin/compose ~/.docker/cli-plugins/docker-compose","handlingStrategy":"validation","validationCode":"name := filepath.Base(path)\nif !strings.HasPrefix(name, \"docker-\") {\n    return fmt.Errorf(\"plugin %q must be named docker-<name>\", name)\n}","typeGuard":null,"tryCatchPattern":"p, err := newPlugin(candidate, cmds)\nif err != nil {\n    continue\n}","preventionTips":["Name all plugin executables with the docker- prefix.","Symlink bare names to docker- prefixed paths if renaming is not possible."],"tags":["cli-plugins","plugins","naming","validation"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}