{"record":{"id":"699a3c2481722495","repo":"moeru-ai/airi","slug":"failed-to-update-character","errorCode":null,"errorMessage":"Failed to update character","messagePattern":"Failed to update character","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/services/characters.ts","lineNumber":199,"sourceCode":"  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> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.characters[':id'].$patch({\n      param: { id },\n      json: payload,\n    }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to update character')\n\n    const data = await res.json()\n    options?.abortSignal?.throwIfAborted()\n    return parse(data)\n  }\n\n  async function removeRemote(client: CharactersRemoteClient, id: string, options?: CharacterServiceOptions): Promise<void> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.characters[':id'].$delete({ param: { id } }, requestOptions(options))\n    if (!res.ok)\n      throw new Error('Failed to remove character')\n    options?.abortSignal?.throwIfAborted()\n  }\n\n  async function likeRemote(client: CharactersRemoteClient, id: string, options?: CharacterServiceOptions): Promise<Character> {\n    options?.abortSignal?.throwIfAborted()\n    const res = await client.api.v1.characters[':id'].like.$post({ param: { id } }, requestOptions(options))\n    if (!res.ok)","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/services/characters.ts#L181-L217","documentation":"Thrown by CharactersService.updateRemote when PATCH /v1/characters/:id returns non-ok. The service sends an UpdateCharacterPayload and re-parses the updated character on success. Failure aborts before parsing, so the caller knows the update was not applied.","triggerScenarios":"client.api.v1.characters[':id'].$patch({ param: { id }, json: payload }) resolves with ok=false. Typical: 400 (invalid update payload), 401/403 (not the owner / not authenticated), 404 (character deleted concurrently), 409 (conflict on unique fields), 500 (server).","commonSituations":"Editing a character another client just deleted (404); submitting invalid field values; permission mismatch (editing someone else's character); stale local copy causing a conflict.","solutions":["Handle 404 by notifying the user the character was removed and refreshing the list.","Confirm ownership/permissions before enabling edit; re-authenticate on 401/403.","Validate the UpdateCharacterPayload fields client-side before PATCHing.","Include res.status and server validation detail in the error for form feedback."],"exampleFix":"// before\nif (!res.ok)\n  throw new Error('Failed to update character')\n\n// after: branch on status\nif (!res.ok) {\n  if (res.status === 404)\n    throw new NotFoundError(`Character ${id} no longer exists`)\n  throw new Error(`Failed to update character ${id} (status ${res.status})`)\n}","handlingStrategy":"try-catch","validationCode":"function validateUpdatePayload(payload: UpdateCharacterPayload): string[] {\n  const errors: string[] = []\n  if ('name' in payload && !payload.name?.trim())\n    errors.push('name cannot be empty')\n  return errors\n}\n\nconst issues = validateUpdatePayload(payload)\nif (issues.length)\n  throw new Error('Invalid update: ' + issues.join(', '))\nawait updateRemote(client, id, payload)","typeGuard":null,"tryCatchPattern":"try {\n  return await updateRemote(client, id, payload)\n}\ncatch (error) {\n  // Patch service to expose status; on 404 refresh list, on 401/403 re-auth, on 422 show field errors.\n  throw error\n}","preventionTips":["Validate UpdateCharacterPayload client-side before PATCHing.","Handle 404 by refreshing the list (character may have been deleted concurrently).","Embed res.status in the service error to enable status-based reconciliation."],"tags":["network","http","characters","api","validation","auth"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}