asdf-vm/asdf · error
is invalid. Name may only contain lowercase letters…
Error message
%s is invalid. Name may only contain lowercase letters, numbers, '_', and '-'
What it means
asdf restricts plugin names to lowercase letters, digits, '_' and '-' (regexp ^[[:lower:][:digit:]_-]+$). validatePluginName runs at the top of Add and Remove; any name with uppercase letters, spaces, dots or other characters is rejected with this message naming the offending name. This keeps plugin names safe as directory names and hook suffixes.
Solutions
- Rename to lowercase, using '-' or '_' as separators (e.g. 'my-plugin')
- Check for stray whitespace or hidden characters in the name argument
- Validate/normalize the name in your own code before calling Add/Remove
Example fix
// before plugins.Add(conf, "NodeJS", url, "", os.Stdout) // after plugins.Add(conf, "nodejs", url, "", os.Stdout)
Defensive patterns
Strategy: validation
Validate before calling
var pluginNameRe = regexp.MustCompile(`^[[:lower:][:digit:]_-]+$`)
if !pluginNameRe.MatchString(name) {
return fmt.Errorf("invalid plugin name: %q", name)
} Type guard
func validPluginName(name string) bool {
return regexp.MustCompile(`^[[:lower:][:digit:]_-]+$`).MatchString(name)
} Prevention
- Normalize names to lowercase with '-'/'_' separators before calling Add/Remove
- Trim whitespace from user-supplied plugin names
When it happens
Trigger: plugins.Add or plugins.Remove (or CLI add/remove) with names containing uppercase letters, spaces, dots (e.g. 'My.Plugin', 'node js'), or other non-matching characters.
Common situations: Copy-pasting a plugin repo name like 'asdf-nodejs' with different casing, using the plugin's display name with spaces, or interpolating a variable that is empty or contains slashes.
Related errors
- command removed
- failed to run pre-download hook
- failed to run pre-install hook
- No such plugin
- plugin not found in repository
AI-assisted analysis of asdf-vm/asdf@074a1722ca (2026-08-30).
Data as JSON: /api/errors/426403db2c92cc08.
Report an issue: GitHub.
Appendix: source
Thrown at internal/plugins/plugins.go:504
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
if err != nil {
return false, err
}
return fileInfo.IsDir(), nil
}
func validatePluginName(name string) error {
match, err := regexp.MatchString("^[[:lower:][:digit:]_-]+$", name)
if err != nil {
return err
}
if !match {
return fmt.Errorf(invalidPluginNameMsg, name)
}
return nil
}
View on GitHub (pinned to 074a1722ca)