chenhg5/cc-connect · error
command %q already exists
Error message
command %q already exists
What it means
AddCommand rejects the operation because a command with the same name already exists in the global commands list. This is a deliberate duplicate-name guard, not an I/O failure. Pick a different name or remove the existing command first.
Source
Thrown at config/config.go:1697
// AddCommand adds a global custom command and persists to config.
func AddCommand(cmd CommandConfig) error {
configMu.Lock()
defer configMu.Unlock()
if ConfigPath == "" {
return fmt.Errorf("config path not set")
}
data, err := os.ReadFile(ConfigPath)
if err != nil {
return fmt.Errorf("read config: %w", err)
}
cfg := &Config{}
if err := toml.Unmarshal(data, cfg); err != nil {
return fmt.Errorf("parse config: %w", err)
}
for _, c := range cfg.Commands {
if c.Name == cmd.Name {
return fmt.Errorf("command %q already exists", cmd.Name)
}
}
cfg.Commands = append(cfg.Commands, cmd)
return saveConfig(cfg)
}
// RemoveCommand removes a global custom command and persists to config.
func RemoveCommand(name string) error {
configMu.Lock()
defer configMu.Unlock()
if ConfigPath == "" {
return fmt.Errorf("config path not set")
}
data, err := os.ReadFile(ConfigPath)
if err != nil {
return fmt.Errorf("read config: %w", err)
}
cfg := &Config{}View on GitHub (pinned to 4000b2338a)
Solutions
- Check existing commands first (config listing API or read config.toml) and skip the add if the name exists for idempotency.
- Use a different, unique name for the new command.
- Remove the existing command with config.RemoveCommand(name) if it should be replaced, then re-add.
- Normalize (trim/lowercase) names before comparing to avoid near-duplicate collisions.
Example fix
// before
config.AddCommand(cmd) // errors if name exists
// after
if err := config.AddCommand(cmd); err != nil {
if strings.Contains(err.Error(), "already exists") {
return nil // idempotent skip
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
cmds, err := config.ListCommands()
if err != nil {
return err
}
for _, c := range cmds {
if c.Name == cmd.Name {
return fmt.Errorf("command %q exists; choose another name", cmd.Name)
}
} Try / catch
err := config.AddCommand(cmd)
if err != nil && strings.Contains(err.Error(), "already exists") {
return nil // treat as idempotent success
} Prevention
- Make install/setup scripts idempotent: skip when the name exists.
- Normalize (trim/lowercase) command names before comparing or storing.
- Show the existing command to the user when a collision is detected.
- Use Remove+Add explicitly when replacement is intended.
When it happens
Trigger: Calling config.AddCommand(cmd) where cmd.Name equals the Name field of any entry already in cfg.Commands after parsing the config (config/config.go:1697).
Common situations: Re-running an install/setup script that adds the same custom command twice; idempotency-ignorant tooling; user re-adding a command they forgot exists; case/whitespace variants being auto-trimmed to a colliding name.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- provider %q already exists in project %q
- global provider %q already exists
- parse existing Agy hooks %s: %w
- marshal Agy hooks overlay: %w
- tmux: 'session' option is required (name of the tmux session
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/40552df033222acf.
Report an issue: GitHub.