moeru-ai/airi · error · Error

Failed to create character

Error message

Failed to create character

What it means

Thrown by CharactersService.createRemote when POST /v1/characters returns non-ok. The service sends a CreateCharacterPayload as JSON and expects a parseable character back. Failure means the server rejected the create (validation, auth, conflict) before a character was persisted.

Source

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

    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> {
    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)

View on GitHub (pinned to 27111382b4)

Solutions

  1. Validate the CreateCharacterPayload client-side (required fields, length limits) before posting.
  2. Re-authenticate if the session expired, then retry the create.
  3. Capture res.status and body to surface field-level validation errors to the form.
  4. On 409 conflict, prompt the user to pick a unique name.

Example fix

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

// after: expose server validation detail
if (!res.ok) {
  const detail = await res.json().catch(() => null)
  throw new CreateCharacterError(`Failed to create character (status ${res.status})`, detail)
}
Defensive patterns

Strategy: validation

Validate before calling

import { CharacterWithRelationsSchema } from '../../types/character'

function validateCreatePayload(payload: CreateCharacterPayload): string[] {
  const errors: string[] = []
  if (!payload.name?.trim())
    errors.push('name is required')
  // add other required-field checks matching server schema
  return errors
}

const issues = validateCreatePayload(payload)
if (issues.length)
  throw new Error('Invalid payload: ' + issues.join(', '))
await createRemote(client, payload)

Try / catch

try {
  return await createRemote(client, payload)
}
catch (error) {
  // Patch service to surface res.status + body; map 400/422 to form field errors.
  // On 401/403 re-authenticate; on 409 prompt for unique name.
  throw error
}

Prevention

When it happens

Trigger: client.api.v1.characters.$post({ json: payload }) resolves with ok=false. Typical: 400 (payload fails server-side validation, e.g. missing required fields, invalid name), 401/403 (not authenticated or lacking create permission), 409 (duplicate slug/name), 500 (server).

Common situations: Submitted character form with missing/invalid fields; session expired mid-edit; backend schema changed and the client payload no longer matches; uniqueness constraint violation on name/slug.

Related errors


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