{"record":{"id":"9aea3c5721dcbc7f","repo":"jlcodes99/cockpit-tools","slug":"accountgroups-failed-to-load-groups-string-er","errorCode":null,"errorMessage":"[AccountGroups] Failed to load groups: ${String(error)}","messagePattern":"\\[AccountGroups\\] Failed to load groups: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/accountGroupService.ts","lineNumber":96,"sourceCode":"      accountIds: [...group.accountIds],\n      // Keep older files without createdAt readable without treating them as\n      // an empty data set.\n      createdAt: typeof group.createdAt === 'number' ? group.createdAt : 0,\n    } as AccountGroup;\n  });\n\n  return cloneGroups(groups);\n}\n\nasync function loadGroupsFromDisk(): Promise<AccountGroup[]> {\n  try {\n    const raw: string = await invoke('load_account_groups');\n    return parseGroups(raw);\n  } catch (error) {\n    if (error instanceof Error && error.message.startsWith('[AccountGroups]')) {\n      throw error;\n    }\n    throw new Error(`[AccountGroups] Failed to load groups: ${String(error)}`);\n  }\n}\n\nasync function saveGroupsToDisk(groups: AccountGroup[]): Promise<void> {\n  try {\n    await invoke('save_account_groups', { data: JSON.stringify(groups, null, 2) });\n  } catch (e) {\n    console.error('[AccountGroups] Failed to save to disk:', e);\n    throw e;\n  }\n}\n\n/** 迁移 localStorage 数据到磁盘（仅首次） */\nasync function migrateLegacyData(): Promise<void> {\n  try {\n    const raw = localStorage.getItem(LEGACY_STORAGE_KEY);\n    if (!raw) return;\n    const legacy = JSON.parse(raw);","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/services/accountGroupService.ts#L78-L114","documentation":"loadGroupsFromDisk wraps any failure from the Tauri 'load_account_groups' invoke or from parseGroups into a uniform '[AccountGroups] Failed to load groups: ...' error. Errors already prefixed with '[AccountGroups]' (e.g. from parseGroups) are re-thrown unchanged to avoid double wrapping. It signals that the persisted account-groups file could not be read or parsed.","triggerScenarios":"The Rust command 'load_account_groups' rejects (file missing, permission denied, backend error) or parseGroups throws a non-prefixed error during JSON parsing/validation of the raw string.","commonSituations":"First run with no groups file on disk, corrupted or hand-edited groups file, backend not compiled/registered for 'load_account_groups', or insufficient file-system permissions.","solutions":["Inspect the wrapped cause in the message (the String(error) suffix) to identify whether the backend invoke or the parser failed","Verify the Rust command 'load_account_groups' is registered and that the groups file path is writable/readable","Delete or repair the corrupted account-groups file so the backend can recreate it","Catch this error in the UI and fall back to an empty group list instead of crashing"],"exampleFix":"// before\nconst groups = await loadGroups();\n// after\nlet groups;\ntry {\n  groups = await loadGroups();\n} catch (error) {\n  console.warn('falling back to empty groups', error);\n  groups = [];\n}","handlingStrategy":"try-catch","validationCode":"// ensure backend capability before call\nif (!('__TAURI__' in window)) throw new Error('backend unavailable');","typeGuard":"function isAccountGroupsError(e: unknown): e is Error {\n  return e instanceof Error && e.message.startsWith('[AccountGroups]');\n}","tryCatchPattern":"try {\n  groups = await loadGroups();\n} catch (e) {\n  if (isAccountGroupsError(e)) {\n    console.error('[AccountGroups]', e.message);\n    groups = []; // or surface e.message to the user\n  } else throw e;\n}","preventionTips":["Include the underlying cause in the wrapped message and log it server-side","Seed a valid default groups file on first run","Add backend tests for load_account_groups with missing and corrupted files","Catch and degrade gracefully in the UI instead of letting it bubble to a crash"],"tags":["file-io","serialization","tauri","backend"],"backgroundTag":"file-read-failed","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}