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
- Free storage and retry the logout
- Verify the config path is writable by the app
- 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
- Monitor free storage; logout writes fail when the disk is full
- After a failed logout, re-run it before considering the profile clean
- Do not treat logout as complete until the save succeeds
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
- failed to switch profile: %w
- failed to list profiles: %w
- failed to get active profile: %w
- failed to resolve active profile %q: %w
- failed to add profile: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/5012d59215ef2f97.
Report an issue: GitHub.