moeru-ai/airi · error · Error
Failed to fetch characters
Error message
Failed to fetch characters
What it means
Thrown by CharactersService.fetchRemote after the GET /v1/characters endpoint returns a non-ok response. The service checks abortSignal before and after the call, then requires res.ok to parse the list. The generic message drops the status code and body, so the caller only knows the list fetch failed.
Source
Thrown at packages/stage-ui/src/services/characters.ts:163
prompts: payload.prompts?.map(prompt => ({
id: nanoid(),
characterId: id,
language: prompt.language,
type: prompt.type,
content: prompt.content,
})),
likes: [],
bookmarks: [],
})
}
async function fetchRemote(client: CharactersRemoteClient, params: { all?: boolean }, options?: CharacterServiceOptions): Promise<Character[]> {
options?.abortSignal?.throwIfAborted()
const res = await client.api.v1.characters.$get({
query: { all: String(params.all ?? false) },
}, requestOptions(options))
if (!res.ok)
throw new Error('Failed to fetch characters')
const data = await res.json()
options?.abortSignal?.throwIfAborted()
return data.map((item: unknown) => parse(item))
}
async function fetchRemoteById(client: CharactersRemoteClient, id: string, options?: CharacterServiceOptions): Promise<Character> {
options?.abortSignal?.throwIfAborted()
const res = await client.api.v1.characters[':id'].$get({ param: { id } }, requestOptions(options))
if (!res.ok)
throw new Error('Failed to fetch character')
const data = await res.json()
options?.abortSignal?.throwIfAborted()
return parse(data)
}
async function createRemote(client: CharactersRemoteClient, payload: CreateCharacterPayload, options?: CharacterServiceOptions): Promise<Character> {View on GitHub (pinned to 27111382b4)
Solutions
- Confirm the user is authenticated and the JWT is valid before calling fetchRemote.
- Verify SERVER_URL points at a backend that serves the /v1/characters route.
- Improve diagnostics by including res.status in the thrown message (the current code discards it).
- Wrap the call in try/catch and fall back to local/cached characters or show an empty state with a retry.
Example fix
// before
if (!res.ok)
throw new Error('Failed to fetch characters')
// after: preserve status for diagnosis
if (!res.ok)
throw new Error(`Failed to fetch characters (status ${res.status})`) Defensive patterns
Strategy: try-catch
Validate before calling
function isAuthenticated(): boolean {
return !!getAuthToken()
}
// before fetching
if (!isAuthenticated()) {
// skip or redirect to login
return []
}
await fetchRemote(client, { all }) Try / catch
try {
const characters = await fetchRemote(client, { all }, { abortSignal })
}
catch (error) {
// Fall back to local/cached characters or show empty state with retry.
// Consider improving the service to include res.status for branching.
return localCache
} Prevention
- Confirm authentication and SERVER_URL before calling fetchRemote.
- Patch the service to embed res.status in the message so callers can branch on 401/500.
- Cache the last successful list so the UI degrades gracefully on failure.
When it happens
Trigger: client.api.v1.characters.$get({ query: { all } }) resolves with ok=false. Typical: 401/403 (not authenticated), 500 (server error), network-layer 0 mapped to a non-ok response, or the backend characters route not deployed at the configured server URL.
Common situations: Logged-out session hitting the characters list; backend API server down or misconfigured; wrong SERVER_URL; expired JWT returning 401; CORS blocking the response so fetch resolves to an opaque/non-ok result.
Related errors
- Failed to fetch character
- Failed to create character
- Failed to update character
- Failed to remove character
- Failed to like character
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/1ec1d33df03f11d2.
Report an issue: GitHub.