moeru-ai/airi · error · Error
voice packs upstream ${res.status}: ${await res.text().catch
Error message
voice packs upstream ${res.status}: ${await res.text().catch(() => '')} What it means
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.
Source
Thrown at packages/stage-ui/src/stores/voice-packs.ts:50
* - The user is authenticated; {@link authedFetch} refreshes an expired access
* token once before surfacing the response.
*
* Returns:
* - Reactive list/error/loading state plus a `load()` action.
*/
export const useVoicePacksStore = defineStore('voice-packs', () => {
const packs = ref<VoicePackListItem[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
async function load() {
loading.value = true
error.value = null
try {
const res = await authedFetch(new URL('/api/v1/voice-packs', SERVER_URL))
if (!res.ok)
throw new Error(`voice packs upstream ${res.status}: ${await res.text().catch(() => '')}`.slice(0, 256))
const data = await res.json() as VoicePackListItem[]
packs.value = data
return data
}
catch (err) {
error.value = errorMessageFrom(err) ?? 'Unknown error'
packs.value = []
return []
}
finally {
loading.value = false
}
}
return { packs, loading, error, load }
})
View on GitHub (pinned to 677329427f)
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/.
Defensive patterns
Strategy: retry
Validate before calling
if (!authStore.user) {
await authClient.getSession() // refresh session before an authed fetch
} Try / catch
const packs = await voicePacksStore.load()
if (voicePacksStore.error?.includes(' 401')) {
await authClient.getSession()
await voicePacksStore.load() // single retry after re-auth
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Gemini TTS request failed: ${response.status} ${await respon
- MiMo TTS request failed: ${response.status} ${response.statu
- Streaming transcription request failed with status ${respons
- audio models upstream ${res.status}: ${await res.text().catc
- audio voices upstream ${res.status}: ${await res.text().catc
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/d88c42bd36948d8b.
Report an issue: GitHub.