netbirdio/netbird · error

failed to remove profile: %w

Error message

failed to remove profile: %w

What it means

RemoveProfile deletes the profile through ServiceManager.RemoveProfile from profiles/. It fails when the id is not a valid filename stem, the profile file cannot be removed (I/O error), or related internal steps fail. The subsequent removal of the stored account email is deliberately best-effort and only logged, never returned, so this error always concerns the profile deletion itself.

Source

Thrown at client/android/profile_manager.go:233

func (pm *ProfileManager) RenameProfile(id string, newName string) error {
	if err := pm.serviceMgr.RenameProfile(profilemanager.ID(id), androidUsername, newName); err != nil {
		return fmt.Errorf("failed to rename profile: %w", err)
	}

	log.Infof("renamed profile %s to: %s", id, newName)
	return nil
}

// RemoveProfile deletes a profile
func (pm *ProfileManager) RemoveProfile(id string) error {
	configPath, err := pm.getProfileConfigPath(id)
	if err != nil {
		return err
	}

	// Use ServiceManager (removes profile from profiles/ directory)
	if err := pm.serviceMgr.RemoveProfile(profilemanager.ID(id), androidUsername); err != nil {
		return fmt.Errorf("failed to remove profile: %w", err)
	}

	// The account file is this package's, not the ServiceManager's, so it must
	// go here. The default profile has a fixed filename, so a recreated one
	// would otherwise inherit the deleted profile's email as its login_hint.
	// Not fatal: the profile itself is gone.
	if err := removeProfileEmail(configPath); err != nil {
		log.Warnf("failed to remove stored account email for profile %s: %v", id, err)
	}

	log.Infof("removed profile: %s", id)
	return nil
}

// getProfileConfigPath returns the config file path for a profile
// This is needed for Android-specific path handling (netbird.cfg for default profile)
func (pm *ProfileManager) getProfileConfigPath(id string) (string, error) {
	if !profilemanager.IsValidProfileFilenameStem(profilemanager.ID(id)) {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Refresh ListProfiles and remove by a current id
  2. Treat a repeat removal of an already-gone profile as success in the UI
  3. Retry on transient I/O errors after checking storage

Example fix

// before: every tap fires a removal
removeBtn.setOnClickListener { pm.RemoveProfile(id) }

// after: ignore the error when the profile is already gone
if err := pm.RemoveProfile(id); err != nil {
	if !profileStillExists(pm, id) { /* already removed: treat as success */ }
}
Defensive patterns

Strategy: validation

Validate before calling

// Refresh and confirm the profile still exists before removing
list, err := pm.ListProfiles()
if err == nil && !containsId(list, id) {
	// nothing to remove; skip the call
}

Try / catch

if err := pm.RemoveProfile(id); err != nil {
	list, lerr := pm.ListProfiles()
	if lerr == nil && !containsId(list, id) {
		// already gone: treat as success
	} else {
		// real removal failure: retry after checking storage
	}
}

Prevention

When it happens

Trigger: RemoveProfile with an invalid id, a profile whose file is locked or unreadable, or an id already removed (repeat removal). The default profile, whose config lives at configDir/netbird.cfg rather than under profiles/, is not managed by this directory path.

Common situations: Double-tap on remove where the second call finds nothing, stale list entry after files changed externally, file locked by a concurrent read.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/8eb26948e5edf0c3. Report an issue: GitHub.