{"record":{"id":"849b3a9826b24746","repo":"moeru-ai/airi","slug":"failed-to-fetch-character","errorCode":null,"errorMessage":"Failed to fetch character","messagePattern":"Failed to fetch character","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/services/characters.ts","lineNumber":174,"sourceCode":"\n  async function fetchRemote(client: CharactersRemoteClient, params: { all?: boolean }, options?: CharacterServiceOptions): Promise<Character[]> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.characters.$get({\n      query: { all: String(params.all ?? false) },\n    }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to fetch characters')\n\n    const data = await res.json()\n    options?.abortSignal?.throwIfAborted()\n    return data.map((item: unknown) => parse(item))\n  }\n\n  async function fetchRemoteById(client: CharactersRemoteClient, id: string, options?: CharacterServiceOptions): Promise<Character> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.characters[':id'].$get({ param: { id } }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to fetch character')\n\n    const data = await res.json()\n    options?.abortSignal?.throwIfAborted()\n    return parse(data)\n  }\n\n  async function createRemote(client: CharactersRemoteClient, payload: CreateCharacterPayload, options?: CharacterServiceOptions): Promise<Character> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.characters.$post({ json: payload }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to create character')\n\n    const data = await res.json()\n    options?.abortSignal?.throwIfAborted()\n    return parse(data)\n  }\n\n  async function updateRemote(client: CharactersRemoteClient, id: string, payload: UpdateCharacterPayload, options?: CharacterServiceOptions): Promise<Character> {","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/services/characters.ts#L156-L192","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif (!res.ok)\n  throw new Error('Failed to fetch character')\n\n// after: branch on status\nif (!res.ok) {\n  if (res.status === 404)\n    throw new NotFoundError(`Character ${id} not found`)\n  throw new Error(`Failed to fetch character ${id} (status ${res.status})`)\n}","handlingStrategy":"try-catch","validationCode":"function isValidCharacterId(id: string): boolean {\n  return typeof id === 'string' && id.length > 0\n}\n\n// before fetching\nif (!isValidCharacterId(id))\n  throw new Error(`Invalid character id: ${id}`)\nawait fetchRemoteById(client, id)","typeGuard":null,"tryCatchPattern":"try {\n  return await fetchRemoteById(client, id)\n}\ncatch (error) {\n  // Patch service to expose status; treat likely-404 as 'removed' and update local store.\n  if (String(error).includes('Failed to fetch character')) {\n    removeCharacterLocally(id)\n  }\n  throw error\n}","preventionTips":["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."],"tags":["network","http","characters","api","auth","not-found"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}