caddyserver/caddy · critical
command already registered: %s
Error message
command already registered: %s
What it means
RegisterCommand panics when a command Name collides with one already registered. Command names are a flat, global namespace (commands map guarded by commandsMu), so two plugins — or a plugin and a built-in like 'start' — cannot both claim the same name.
Source
Thrown at cmd/commands.go:592
// if the name does not meet the described format, or if
// any of the fields are missing from cmd.
//
// This function should be used in init().
func RegisterCommand(cmd Command) {
commandsMu.Lock()
defer commandsMu.Unlock()
if cmd.Name == "" {
panic("command name is required")
}
if cmd.Func == nil && cmd.CobraFunc == nil {
panic("command function missing")
}
if cmd.Short == "" {
panic("command short string is required")
}
if _, exists := commands[cmd.Name]; exists {
panic("command already registered: " + cmd.Name)
}
if !commandNameRegex.MatchString(cmd.Name) {
panic("invalid command name")
}
defaultFactory.Use(func(rootCmd *cobra.Command) {
rootCmd.AddCommand(caddyCmdToCobra(cmd))
})
commands[cmd.Name] = cmd
}
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
View on GitHub (pinned to 50e54ee279)
Solutions
- Rename your plugin's command to something unique (vendor-prefix it, e.g. 'acme-verify')
- Remove the duplicate plugin from the build
- Check existing names with 'caddy help' in a build without your plugin before choosing
Example fix
// before
caddy.RegisterCommand(caddy.Command{Name: "list", ...}) // collides with built-in
// after
caddy.RegisterCommand(caddy.Command{Name: "myplugin-list", ...}) Defensive patterns
Strategy: validation
Validate before calling
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
if !commandNameRegex.MatchString(name) {
return fmt.Errorf("command name %q invalid", name)
} Prevention
- Vendor-prefix plugin command names to avoid collisions with built-ins
- Check 'caddy help' output before picking a name
When it happens
Trigger: A plugin registers a command named e.g. "list" or "adapt" that caddy's cmd package (or another loaded plugin) already registered; the second RegisterCommand call panics during init.
Common situations: A custom build with several plugins where two expose the same subcommand; a plugin reusing a built-in name ('reload', 'stop', 'validate'); two versions of the same plugin accidentally linked in.
Related errors
- invalid command name
- command short string is required
- module %s is not a precompressor; is %T
- field %s does not exist in %#v
- network type %s is reserved
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/b63296731b735248.
Report an issue: GitHub.