{"record":{"id":"d88c42bd36948d8b","repo":"moeru-ai/airi","slug":"voice-packs-upstream-res-status-await-res-te","errorCode":null,"errorMessage":"voice packs upstream ${res.status}: ${await res.text().catch(() => '')}","messagePattern":"voice packs upstream (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/stores/voice-packs.ts","lineNumber":50,"sourceCode":" * - The user is authenticated; {@link authedFetch} refreshes an expired access\n *   token once before surfacing the response.\n *\n * Returns:\n * - Reactive list/error/loading state plus a `load()` action.\n */\nexport const useVoicePacksStore = defineStore('voice-packs', () => {\n  const packs = ref<VoicePackListItem[]>([])\n  const loading = ref(false)\n  const error = ref<string | null>(null)\n\n  async function load() {\n    loading.value = true\n    error.value = null\n\n    try {\n      const res = await authedFetch(new URL('/api/v1/voice-packs', SERVER_URL))\n      if (!res.ok)\n        throw new Error(`voice packs upstream ${res.status}: ${await res.text().catch(() => '')}`.slice(0, 256))\n\n      const data = await res.json() as VoicePackListItem[]\n      packs.value = data\n      return data\n    }\n    catch (err) {\n      error.value = errorMessageFrom(err) ?? 'Unknown error'\n      packs.value = []\n      return []\n    }\n    finally {\n      loading.value = false\n    }\n  }\n\n  return { packs, loading, error, load }\n})\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/stores/voice-packs.ts#L32-L68","documentation":"load() calls authedFetch GET ${SERVER_URL}/api/v1/voice-packs; any non-OK status throws with the HTTP code plus up to 256 chars of the response body. The store catches it into error.value and empties packs, so the failure surfaces via store state rather than an unhandled rejection.","triggerScenarios":"401/403 when the auth session expired or authedFetch attached no valid token; 404 when SERVER_URL points at a backend without the voice-packs route; 5xx when the API server is down or erroring.","commonSituations":"Long-idle tab with an expired session; dev SERVER_URL env pointing at the wrong origin; frontend deployed against an older API build; the server/ docker-compose stack not started.","solutions":["If 401/403: re-authenticate, then call load() again.","Confirm SERVER_URL resolves to an API that serves /api/v1/voice-packs (curl it directly).","Read the sliced body embedded in error.value for the backend's own error detail.","If 5xx or unreachable: start or restart the server stack under server/."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if (!authStore.user) {\n  await authClient.getSession() // refresh session before an authed fetch\n}","typeGuard":null,"tryCatchPattern":"const packs = await voicePacksStore.load()\nif (voicePacksStore.error?.includes(' 401')) {\n  await authClient.getSession()\n  await voicePacksStore.load() // single retry after re-auth\n}","preventionTips":["Centralize 401 handling in authedFetch (auto-refresh plus one retry).","Render voicePacksStore.error in the UI instead of showing silently empty lists.","Health-check SERVER_URL at app boot."],"tags":["http","api","voice-packs","auth","fetch"],"backgroundTag":"http-error-response","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}