moeru-ai/airi · error · Error
Failed to fetch character
Error message
Failed to fetch character
What it means
Thrown by CharactersService.fetchRemoteById when GET /v1/characters/:id returns non-ok. The service parses a single character via the valibot CharacterWithRelationsSchema on success; on failure it aborts before parsing. Like the list fetch, the status code is discarded in the message.
Source
Thrown at packages/stage-ui/src/services/characters.ts:174
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> {
options?.abortSignal?.throwIfAborted()
const res = await client.api.v1.characters.$post({ json: payload }, requestOptions(options))
if (!res.ok)
throw new Error('Failed to create character')
const data = await res.json()
options?.abortSignal?.throwIfAborted()
return parse(data)
}
async function updateRemote(client: CharactersRemoteClient, id: string, payload: UpdateCharacterPayload, options?: CharacterServiceOptions): Promise<Character> {View on GitHub (pinned to 27111382b4)
Solutions
- Handle 404 distinctly: remove the character from the local store and notify the user it no longer exists.
- Confirm the id is well-formed and exists (e.g. was returned by a prior list fetch).
- Include res.status in the error message to enable status-based handling upstream.
- Retry once on 5xx before surfacing the failure.
Example fix
// before
if (!res.ok)
throw new Error('Failed to fetch character')
// after: branch on status
if (!res.ok) {
if (res.status === 404)
throw new NotFoundError(`Character ${id} not found`)
throw new Error(`Failed to fetch character ${id} (status ${res.status})`)
} Defensive patterns
Strategy: try-catch
Validate before calling
function isValidCharacterId(id: string): boolean {
return typeof id === 'string' && id.length > 0
}
// before fetching
if (!isValidCharacterId(id))
throw new Error(`Invalid character id: ${id}`)
await fetchRemoteById(client, id) Try / catch
try {
return await fetchRemoteById(client, id)
}
catch (error) {
// Patch service to expose status; treat likely-404 as 'removed' and update local store.
if (String(error).includes('Failed to fetch character')) {
removeCharacterLocally(id)
}
throw error
} Prevention
- Handle 404 distinctly by removing the stale character from local state.
- Embed res.status in the service error so callers can branch.
- Validate the id came from a prior successful list fetch.
When it happens
Trigger: client.api.v1.characters[':id'].$get({ param: { id } }) resolves with ok=false. Most common: 404 (character was deleted or id is wrong), 401/403 (auth), 500 (server). Also when a just-deleted character is still referenced in the UI and a refetch is attempted.
Common situations: Stale character id in a deep link or store; concurrent deletion by another client; permission to list but not to view a specific character; transient server error.
Related errors
- Failed to remove character
- Failed to fetch characters
- Failed to create character
- Failed to update character
- Failed to like character
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/849b3a9826b24746.
Report an issue: GitHub.