moeru-ai/airi · error · Error

Failed to update character

Error message

Failed to update character

What it means

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.

Source

Thrown at packages/stage-ui/src/services/characters.ts:199

  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> {
    options?.abortSignal?.throwIfAborted()
    const res = await client.api.v1.characters[':id'].$patch({
      param: { id },
      json: payload,
    }, requestOptions(options))
    if (!res.ok)
      throw new Error('Failed to update character')

    const data = await res.json()
    options?.abortSignal?.throwIfAborted()
    return parse(data)
  }

  async function removeRemote(client: CharactersRemoteClient, id: string, options?: CharacterServiceOptions): Promise<void> {
    options?.abortSignal?.throwIfAborted()
    const res = await client.api.v1.characters[':id'].$delete({ param: { id } }, requestOptions(options))
    if (!res.ok)
      throw new Error('Failed to remove character')
    options?.abortSignal?.throwIfAborted()
  }

  async function likeRemote(client: CharactersRemoteClient, id: string, options?: CharacterServiceOptions): Promise<Character> {
    options?.abortSignal?.throwIfAborted()
    const res = await client.api.v1.characters[':id'].like.$post({ param: { id } }, requestOptions(options))
    if (!res.ok)

View on GitHub (pinned to 27111382b4)

Solutions

  1. Handle 404 by notifying the user the character was removed and refreshing the list.
  2. Confirm ownership/permissions before enabling edit; re-authenticate on 401/403.
  3. Validate the UpdateCharacterPayload fields client-side before PATCHing.
  4. Include res.status and server validation detail in the error for form feedback.

Example fix

// before
if (!res.ok)
  throw new Error('Failed to update character')

// after: branch on status
if (!res.ok) {
  if (res.status === 404)
    throw new NotFoundError(`Character ${id} no longer exists`)
  throw new Error(`Failed to update character ${id} (status ${res.status})`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

function validateUpdatePayload(payload: UpdateCharacterPayload): string[] {
  const errors: string[] = []
  if ('name' in payload && !payload.name?.trim())
    errors.push('name cannot be empty')
  return errors
}

const issues = validateUpdatePayload(payload)
if (issues.length)
  throw new Error('Invalid update: ' + issues.join(', '))
await updateRemote(client, id, payload)

Try / catch

try {
  return await updateRemote(client, id, payload)
}
catch (error) {
  // Patch service to expose status; on 404 refresh list, on 401/403 re-auth, on 422 show field errors.
  throw error
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/699a3c2481722495. Report an issue: GitHub.