{"record":{"id":"5012d59215ef2f97","repo":"netbirdio/netbird","slug":"failed-to-save-config-w","errorCode":null,"errorMessage":"failed to save config: %w","messagePattern":"failed to save config: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/android/profile_manager.go","lineNumber":201,"sourceCode":"\n\t// Check if profile exists\n\tif _, err := os.Stat(configPath); os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"profile '%s' does not exist\", id)\n\t}\n\n\t// Read current config using internal profilemanager\n\tconfig, err := profilemanager.ReadConfig(configPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read profile config: %w\", err)\n\t}\n\n\t// Clear authentication by removing private key and SSH key\n\tconfig.PrivateKey = \"\"\n\tconfig.SSHKey = \"\"\n\n\t// Save config using internal profilemanager\n\tif err := profilemanager.WriteOutConfig(configPath, config); err != nil {\n\t\treturn fmt.Errorf(\"failed to save config: %w\", err)\n\t}\n\n\t// The stored account email is kept on purpose, matching the desktop and CLI\n\t// logout semantics: the next login passes it as the login_hint so the IdP\n\t// preselects the account. Removing the profile is what deletes it.\n\tlog.Infof(\"logged out from profile: %s\", id)\n\treturn nil\n}\n\n// RenameProfile changes a profile's display name. The profile ID, and therefore\n// its on-disk filename, is left untouched: only the \"name\" field of the config\n// is rewritten. This works for the default profile too, whose config lives in\n// netbird.cfg rather than under profiles/.\nfunc (pm *ProfileManager) RenameProfile(id string, newName string) error {\n\tif err := pm.serviceMgr.RenameProfile(profilemanager.ID(id), androidUsername, newName); err != nil {\n\t\treturn fmt.Errorf(\"failed to rename profile: %w\", err)\n\t}\n","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/android/profile_manager.go#L183-L219","documentation":"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.","triggerScenarios":"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.","commonSituations":"Full device storage (common on Android TV boxes), permissions disturbed by a restore, heavy concurrent disk use during logout.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before logout, confirm space and writability\nvar st syscall.Statfs_t\nif err := syscall.Statfs(configDir, &st); err == nil && st.Bavail*uint64(st.Bsize) < 1<<20 {\n\t// under 1 MiB free: the config save will likely fail\n}","typeGuard":null,"tryCatchPattern":"if err := pm.LogoutProfile(id); err != nil {\n\tif strings.Contains(err.Error(), \"failed to save config\") {\n\t\t// free storage, then retry; verify the saved file has an empty private key afterwards\n\t}\n}","preventionTips":["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"],"tags":["android","go","profile","io","logout"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}