{"record":{"id":"6ebb6de9b6d2c39a","repo":"jlcodes99/cockpit-tools","slug":"accountstore-6ebb6d","errorCode":null,"errorMessage":"[AccountStore] 忽略异常空账号列表，保留本地缓存账号","messagePattern":"\\[AccountStore\\] 忽略异常空账号列表，保留本地缓存账号","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/stores/useAccountStore.ts","lineNumber":205,"sourceCode":"          const now = Date.now();\n          \n          // 如果正在请求中，且距离上次请求不足 DEBOUNCE_MS，复用现有 Promise\n          if (fetchAccountsPromise && now - fetchAccountsLastTime < DEBOUNCE_MS) {\n              return fetchAccountsPromise;\n          }\n          \n          fetchAccountsLastTime = now;\n          \n          fetchAccountsPromise = (async () => {\n              const requestId = ++fetchAccountsSeq;\n              set({ loading: true, error: null });\n              try {\n                  const accounts = await accountService.listAccounts();\n                  if (requestId !== fetchAccountsSeq) {\n                      return;\n                  }\n                  if (accounts.length === 0 && get().accounts.length > 0 && !allowNextEmptyAccountList) {\n                      console.warn('[AccountStore] 忽略异常空账号列表，保留本地缓存账号');\n                      set({ loading: false });\n                      return;\n                  }\n                  allowNextEmptyAccountList = false;\n                  set({ accounts, loading: false });\n              } catch (e) {\n                  if (requestId !== fetchAccountsSeq) {\n                      return;\n                  }\n                  set({ error: String(e), loading: false });\n              } finally {\n                  if (requestId === fetchAccountsSeq) {\n                      allowNextEmptyAccountList = false;\n                  }\n                  // 请求完成后延迟清除 Promise，允许短时间内的后续调用也复用结果\n                  setTimeout(() => {\n                      if (requestId === fetchAccountsSeq) {\n                          fetchAccountsPromise = null;","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/stores/useAccountStore.ts#L187-L223","documentation":"A defensive console.warn in useAccountStore's fetchAccounts: accountService.listAccounts() resolved to an empty array while the store already holds cached accounts and no explicit allowNextEmptyAccountList flag was set. The store keeps the cached accounts and aborts the update, guarding against backend responses that are spuriously empty.","triggerScenarios":"listAccounts() returns [] while get().accounts.length > 0 and allowNextEmptyAccountList is false, with the request still current (requestId check passed).","commonSituations":"Backend session silently expired so the list comes back empty instead of 401; provider API change altering the list endpoint shape; backend restart mid-request; network middleware dropping the payload body.","solutions":["Retry fetching accounts; transient empty responses are safely ignored.","Re-authenticate if the backend session expired — verify with an authenticated endpoint.","If accounts were genuinely removed, use the store's logout/reset path which sets allowNextEmptyAccountList.","Inspect the raw listAccounts response for API shape/pagination regressions.","Clear the local cache deliberately if it is confirmed stale."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const accounts = await accountService.listAccounts();\nif (accounts.length === 0 && get().accounts.length > 0 && !allowNextEmptyAccountList) {\n  console.warn('[AccountStore] ignoring anomalous empty account list');\n  set({ loading: false });\n  return;\n}","typeGuard":"function isNonEmptyAccounts(v: unknown): v is Account[] {\n  return Array.isArray(v) && v.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Always route intentional empty transitions through allowNextEmptyAccountList.","Check authentication state before fetching; an empty list often hides an expired session.","Keep a last-known-good snapshot and timestamp for diagnostics when the guard trips."],"tags":["state-management","cache","accounts","guard"],"backgroundTag":"empty-account-list-guard","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"}