wavetermdev/waveterm · error
draft app '%s' already exists
Error message
draft app '%s' already exists
What it means
Renames are also blocked if the new name already exists in the draft namespace. When os.Stat(newDraftDir) succeeds, RenameLocalApp returns "draft app '%s' already exists" so a local rename cannot silently collide with an in-progress draft of the same name.
Source
Thrown at pkg/waveappstore/waveappstore.go:694
if _, err := os.Stat(oldDraftDir); err == nil {
draftExists = true
} else if !os.IsNotExist(err) {
return fmt.Errorf("failed to check draft app: %w", err)
}
if !localExists && !draftExists {
return fmt.Errorf("app '%s' does not exist in local or draft namespace", appName)
}
// Check if new app name already exists in either namespace
if _, err := os.Stat(newLocalDir); err == nil {
return fmt.Errorf("local app '%s' already exists", newAppName)
} else if !os.IsNotExist(err) {
return fmt.Errorf("failed to check if new local app exists: %w", err)
}
if _, err := os.Stat(newDraftDir); err == nil {
return fmt.Errorf("draft app '%s' already exists", newAppName)
} else if !os.IsNotExist(err) {
return fmt.Errorf("failed to check if new draft app exists: %w", err)
}
// Rename local app if it exists
if localExists {
if err := os.Rename(oldLocalDir, newLocalDir); err != nil {
return fmt.Errorf("failed to rename local app: %w", err)
}
}
// Rename draft app if it exists
if draftExists {
if err := os.Rename(oldDraftDir, newDraftDir); err != nil {
// If local was renamed but draft fails, try to rollback local rename
if localExists {
if rollbackErr := os.Rename(newLocalDir, oldLocalDir); rollbackErr != nil {
return fmt.Errorf("failed to rename draft app (and failed to rollback local rename: %v): %w", rollbackErr, err)View on GitHub (pinned to a4447c1563)
Solutions
- Choose a newName not present in either the local or draft namespace
- Delete the stale draft directory (~/waveapps/draft/<newName>) if it is no longer needed
- Publish or clean up the existing draft first, then retry the rename
Example fix
// before
RenameLocalApp("myapp", "wipapp") // draft:wipapp exists
// after
os.RemoveAll(filepath.Join(home, "waveapps", "draft", "wipapp")) // only if stale
RenameLocalApp("myapp", "wipapp") Defensive patterns
Strategy: validation
Validate before calling
func draftNameTaken(name string) bool {
_, err := os.Stat(filepath.Join(wavebase.GetHomeDir(), "waveapps", "draft", name))
return err == nil
}
// require !draftNameTaken(newName) before renaming Try / catch
if err := waveappstore.RenameLocalApp(oldName, newName); err != nil {
if strings.Contains(err.Error(), "draft app") && strings.Contains(err.Error(), "already exists") {
// prompt user to resolve/remove the draft or pick another name
return fmt.Errorf("a draft named %q exists; resolve it first", newName)
}
return err
} Prevention
- Check both local and draft namespaces for the target name before renaming
- Clean up stale drafts after failed publish workflows
- Prefix ad-hoc draft names so they are easy to identify and prune
When it happens
Trigger: Calling RenameLocalApp(oldName, newName) where ~/waveapps/draft/<newName> exists — e.g. a draft with that name was created earlier or a previous draft/publish cycle left the directory behind.
Common situations: Renaming to a name that was previously drafted but never published, leftover draft directories after interrupted workflows, or intentional drafts the user forgot about.
Related errors
- local app '%s' already exists
- invalid app name: %w
- invalid new app name: %w
- app '%s' does not exist in local or draft namespace
- failed to rename temp file %q to %q: %w (also failed to remo
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/94d22d637d619314.
Report an issue: GitHub.