{"record":{"id":"242bde14c2fb0b37","repo":"docker/cli","slug":"plugin-candidate-q-w","errorCode":null,"errorMessage":"plugin candidate %q: %w","messagePattern":"plugin candidate %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli-plugins/manager/plugin.go","lineNumber":75,"sourceCode":"// Plugin.  If the candidate fails one of the tests then `Plugin.Err`\n// is set, and is always a `pluginError`, but the `Plugin` is still\n// returned with no error. An error is only returned due to a\n// 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","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli-plugins/manager/plugin.go#L57-L93","documentation":"Returned by newPlugin when trimExeSuffix fails for the candidate. On Windows trimExeSuffix requires a .exe extension; on other platforms it is a no-op. The underlying trimExeSuffix error is wrapped with the candidate path for context.","triggerScenarios":"On Windows, a plugin candidate whose filename lacks the .exe extension. On non-Windows this branch is effectively unreachable because trimExeSuffix always succeeds.","commonSituations":"A plugin binary built/copied on Windows without the .exe suffix, or a script file placed in the plugin directory without the expected extension.","solutions":["On Windows, ensure the plugin binary has a .exe extension.","Rebuild the plugin for Windows so the produced artifact carries .exe.","Rename the file to append .exe if it is a valid executable."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"p, err := newPlugin(candidate, cmds)\nif err != nil {\n    continue // skip candidate\n}","preventionTips":["On Windows, ensure plugin binaries carry the .exe extension.","Build plugins with GOOS=windows for Windows deployments."],"tags":["cli-plugins","plugins","windows","filesystem"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}