netbirdio/netbird · error

failed to save config: %w

Error message

failed to save config: %w

What it means

After clearing PrivateKey and SSHKey in memory, WriteOutConfig failed to persist the logged-out config to disk. This is a pure I/O failure: unwritable path, read-only filesystem, or no space left. The on-disk config may still contain the old keys until a later write succeeds, so logout is not complete when this error returns.

Source

Thrown at client/android/profile_manager.go:201

	// Check if profile exists
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		return fmt.Errorf("profile '%s' does not exist", id)
	}

	// Read current config using internal profilemanager
	config, err := profilemanager.ReadConfig(configPath)
	if err != nil {
		return fmt.Errorf("failed to read profile config: %w", err)
	}

	// Clear authentication by removing private key and SSH key
	config.PrivateKey = ""
	config.SSHKey = ""

	// Save config using internal profilemanager
	if err := profilemanager.WriteOutConfig(configPath, config); err != nil {
		return fmt.Errorf("failed to save config: %w", err)
	}

	// The stored account email is kept on purpose, matching the desktop and CLI
	// logout semantics: the next login passes it as the login_hint so the IdP
	// preselects the account. Removing the profile is what deletes it.
	log.Infof("logged out from profile: %s", id)
	return nil
}

// RenameProfile changes a profile's display name. The profile ID, and therefore
// its on-disk filename, is left untouched: only the "name" field of the config
// is rewritten. This works for the default profile too, whose config lives in
// netbird.cfg rather than under profiles/.
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)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Free storage and retry the logout
  2. Verify the config path is writable by the app
  3. After a successful retry, confirm the file no longer contains a private key before treating logout as complete
Defensive patterns

Strategy: try-catch

Validate before calling

// Before logout, confirm space and writability
var st syscall.Statfs_t
if err := syscall.Statfs(configDir, &st); err == nil && st.Bavail*uint64(st.Bsize) < 1<<20 {
	// under 1 MiB free: the config save will likely fail
}

Try / catch

if err := pm.LogoutProfile(id); err != nil {
	if strings.Contains(err.Error(), "failed to save config") {
		// free storage, then retry; verify the saved file has an empty private key afterwards
	}
}

Prevention

When it happens

Trigger: LogoutProfile when configDir is out of space, the config file is read-only or locked, or storage is temporarily unavailable at the moment of the save.

Common situations: Full device storage (common on Android TV boxes), permissions disturbed by a restore, heavy concurrent disk use during logout.

Related errors


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