{"record":{"id":"5f4706183a3f15e0","repo":"Molunerfinn/PicGo","slug":"result-error-dynamic-message-propagated-from-rpc","errorCode":null,"errorMessage":"result.error (dynamic message propagated from RPC failure)","messagePattern":"result\\.error \\(dynamic message propagated from RPC failure\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/renderer/queries/picgo-cloud-album-stats.ts","lineNumber":13,"sourceCode":"import { useQuery } from '@tanstack/react-query'\nimport type { CloudAlbumStatsResponse } from '#/types/cloudAlbum'\nimport { cloudAlbumAdapter } from '@/adapters/cloud-album'\nimport { rendererQueryClient } from './query-client'\n\nexport const PicGoCloudAlbumStatsQueryKeys = {\n  stats: ['picgo-cloud-album', 'stats'] as const\n}\n\nasync function fetchCloudAlbumStats (): Promise<CloudAlbumStatsResponse> {\n  const result = await cloudAlbumAdapter.getStats()\n  if (!result.success) {\n    throw new Error(result.error)\n  }\n  return result.data\n}\n\nexport function useCloudAlbumStatsQuery (options?: { enabled?: boolean }) {\n  return useQuery({\n    queryKey: PicGoCloudAlbumStatsQueryKeys.stats,\n    queryFn: fetchCloudAlbumStats,\n    refetchOnWindowFocus: true,\n    enabled: options?.enabled ?? true\n  })\n}\n\nexport async function invalidateCloudAlbumStatsQuery () {\n  await rendererQueryClient.invalidateQueries({\n    queryKey: PicGoCloudAlbumStatsQueryKeys.stats\n  })\n}","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/Molunerfinn/PicGo/blob/07ec7068a5512344da093d49a8408fa63ba3421e/src/renderer/queries/picgo-cloud-album-stats.ts#L1-L31","documentation":"fetchCloudAlbumStats throws Error(result.error) when the PICGO_CLOUD_ALBUM_GET_STATS RPC invoked via cloudAlbumAdapter.getStats() returns success:false. The library uses this pattern to convert RPC failure envelopes into thrown errors so TanStack Query records the fetch as failed. The message is the dynamic error text produced by the main-process RPC handler, so its content is not fixed.","triggerScenarios":"The RPC handler for cloud album stats fails: user not logged in to PicGo Cloud, expired/invalid token, network failure reaching picgo-hub, lifecycle restriction (grace/frozen/plan required, e.g. GRACE_RESTRICTED / QUOTA_EXCEEDED codes), or main-process handler throwing and serializing its error message into result.error.","commonSituations":"Opening the cloud album stats panel while offline or with an expired session; free-plan user triggering stats that require a paid plan; token revoked after password change; server 5xx during picgo-hub maintenance.","solutions":["Check the error message in the query (query.error.message) — it names the actual RPC failure (auth, network, or plan code).","If it is auth-related, re-run the PicGo Cloud login flow (cloudAdapter.login) and invalidate the query.","If it is a plan/quota code (PLAN_REQUIRED, QUOTA_EXCEEDED, GRACE_RESTRICTED), resolve it via resolveCloudErrorMessage and upgrade/renew the plan.","For network errors, restore connectivity and refetch; the query key ['picgo-cloud-album','stats'] can be invalidated manually.","If result.error can be undefined, the thrown Error may have an empty message — consider a fallback message like in the plugin actions pattern."],"exampleFix":"// before\nconst result = await cloudAlbumAdapter.getStats()\nif (!result.success) {\n  throw new Error(result.error)\n}\n// after\nconst result = await cloudAlbumAdapter.getStats()\nif (!result.success) {\n  throw new Error(result.error || i18n.t('OPERATION_FAILED'))\n}","handlingStrategy":"try-catch","validationCode":"// Before calling the query, check session state\nconst userInfo = rendererQueryClient.getQueryData<IPicGoCloudUserInfo | null>(['picgo-cloud', 'user-info'])\nif (!userInfo) throw new Error('Not logged in to PicGo Cloud')","typeGuard":"function isRPCSuccess<T>(r: { success: boolean, data?: T, error?: string }): r is { success: true, data: T } {\n  return r.success === true && r.data !== undefined\n}","tryCatchPattern":"const { data, error } = useCloudAlbumStatsQuery()\n// or imperatively:\ntry {\n  const stats = await rendererQueryClient.fetchQuery({ queryKey: ['picgo-cloud-album', 'stats'], queryFn: fetchCloudAlbumStats })\n} catch (e) {\n  const msg = e instanceof Error ? e.message : 'Unknown error'\n  toast.error(resolveCloudErrorMessage(i18n.t, undefined, undefined, msg))\n}","preventionTips":["Gate the query with enabled: !!userInfo so it never runs logged out.","Use TanStack Query retry options for transient network failures only.","Surface error.message through resolveCloudErrorMessage for actionable localized text.","Provide a fallback message when result.error may be undefined."],"tags":["rpc","react-query","network","authentication"],"backgroundTag":"rpc-error-propagation","analyzedSha":"07ec7068a5512344da093d49a8408fa63ba3421e","analyzedAt":"2026-08-30T01:45:22.289Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}