docker/cli · error
unable to determine basename of plugin candidate
Error message
unable to determine basename of plugin candidate %q
What it means
Returned by newPlugin when filepath.Base(path) yields ".", meaning the candidate path has no meaningful basename. The plugin manager treats this as a non-recoverable error because a plugin cannot be identified without a filename. Normally the candidate listing skips such paths, so hitting it indicates an unexpected path shape.
Solutions
- Verify the plugin directory contains real executable files, not '.' or directory entries.
- Remove or fix the offending symlink/path that collapses to '.'.
- Check the candidate path string for trailing separators or empty segments.
Defensive patterns
Strategy: validation
Validate before calling
if filepath.Base(path) == "." {
return fmt.Errorf("plugin path %q has no basename; skip", path)
} Try / catch
p, err := newPlugin(candidate, cmds)
if err != nil {
// skip this degenerate candidate
continue
} Prevention
- Keep the plugin directory free of '.'-like or directory-only entries.
- Validate candidate paths have a real filename component.
When it happens
Trigger: Passing a pluginCandidate whose Path() resolves to a directory-only form like "." or ends in a separator so Base returns ".".
Common situations: A misconfigured plugin directory entry, a symlink resolving to the current directory, or a bug in candidate enumeration producing a degenerate path.
Related errors
- plugin candidate
- plugin candidate : does not have prefix
- path lacks required file extension (.exe)
- source can not be empty
- destination can not be empty
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/a94505d0835cae82.
Report an issue: GitHub.
Appendix: source
Thrown at cli-plugins/manager/plugin.go:71
Metadata() ([]byte, error)
}
// newPlugin determines if the given candidate is valid and returns a
// Plugin. If the candidate fails one of the tests then `Plugin.Err`
// is set, and is always a `pluginError`, but the `Plugin` is still
// returned with no error. An error is only returned due to a
// non-recoverable error.
func newPlugin(c pluginCandidate, cmds []*cobra.Command) (Plugin, error) {
path := c.Path()
if path == "" {
return Plugin{}, errors.New("plugin candidate path cannot be empty")
}
// The candidate listing process should have skipped anything
// which would fail here, so there are all real errors.
fullname := filepath.Base(path)
if fullname == "." {
return Plugin{}, fmt.Errorf("unable to determine basename of plugin candidate %q", path)
}
var err error
if fullname, err = trimExeSuffix(fullname); err != nil {
return Plugin{}, fmt.Errorf("plugin candidate %q: %w", path, err)
}
if !strings.HasPrefix(fullname, metadata.NamePrefix) {
return Plugin{}, fmt.Errorf("plugin candidate %q: does not have %q prefix", path, metadata.NamePrefix)
}
p := Plugin{
Name: strings.TrimPrefix(fullname, metadata.NamePrefix),
Path: path,
}
// Now apply the candidate tests, so these update p.Err.
if !isValidPluginName(p.Name) {
p.Err = newPluginError("plugin candidate %q did not match %q", p.Name, pluginNameFormat)
return p, nilView on GitHub (pinned to 4f84911bfe)