moeru-ai/airi · error · Error

Invalid chat session export format

Error message

Invalid chat session export format

What it means

Thrown by importChatSessions in the data-maintenance composable when the parsed JSON fails isChatSessionsPayload: the top-level `format` field must be the exact string 'chat-sessions-index:v1', matching what chatStore.exportSessions() writes on export. It protects chatStore.importSessions from receiving arbitrary JSON before anything is written to the store. Any other structure — a hand-edited file, a different version's export, or unrelated JSON that happened to parse — is rejected.

Solutions

  1. Re-export chat sessions with the same app version's export button and import that file
  2. Open the JSON and confirm the top level contains the key format set to 'chat-sessions-index:v1'
  3. If migrating an older export, write a one-off converter that sets the v1 format key and the expected sessions structure
  4. Validate the file is complete, parseable JSON before import — truncated downloads fail the check

Example fix

// before
const payload = JSON.parse(await file.text())
await importChatSessions(payload) // throws: Invalid chat session export format

// after
const payload = JSON.parse(await file.text())
if (payload?.format !== 'chat-sessions-index:v1')
  throw new Error('Selected file is not an AIRI chat export (expected chat-sessions-index:v1)')
await importChatSessions(payload)
Defensive patterns

Strategy: type-guard

Validate before calling

const raw = JSON.parse(await file.text())
const isExport = !!raw && typeof raw === 'object' && raw.format === 'chat-sessions-index:v1'
if (!isExport)
  throw new Error('Not a chat sessions export')

Type guard

function isChatSessionsPayload(payload: unknown): payload is ChatSessionsExport {
  return !!payload && typeof payload === 'object'
    && (payload as { format?: string }).format === 'chat-sessions-index:v1'
}

Try / catch

try {
  await importChatSessions(payload)
}
catch (err) {
  // Distinguish format rejection from store failures so the UI tells the
  // user to re-check the selected file instead of reporting a data bug.
  toast(err instanceof Error && err.message === 'Invalid chat session export format'
    ? 'Selected file is not a chat session export'
    : 'Import failed')
}

Prevention

When it happens

Trigger: Importing a file that was not produced by the matching export function: an older or newer app version's export carrying a different format marker, a manually authored file, a truncated download, or a single-session export instead of the full sessions index.

Common situations: User picks the wrong file in the import picker; migrating chats between machines running different app versions; editing the exported JSON by hand; importing an export produced by a different AIRI stage app whose store writes another format key.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-08-18). Data as JSON: /api/errors/564acd6f7ad23833. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-ui/src/composables/use-data-maintenance.ts:99

  function deleteAllChatSessions() {
    chatOrchestrator.cancelPendingSends()
    chatStore.resetAllSessions()
  }

  async function exportChatSessions() {
    const data = await chatStore.exportSessions()
    return new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
  }

  function isChatSessionsPayload(payload: unknown): payload is ChatSessionsExport {
    if (!payload || typeof payload !== 'object')
      return false
    return (payload as { format?: string }).format === 'chat-sessions-index:v1'
  }

  async function importChatSessions(payload: Record<string, unknown>) {
    if (!isChatSessionsPayload(payload))
      throw new Error('Invalid chat session export format')
    await chatStore.importSessions(payload)
    return payload
  }

  async function resetSettingsState() {
    await settingsStore.resetState()
    audioSettingsStore.resetState()
    live2dParamsStore.resetState()
    live2dSettingsStore.resetState()
    live2dMagicSettingsStore.resetState()
    threeStore.resetModelStore()
    mcpStore.resetState()
    onboardingStore.resetSetupState()
    airiCardStore.resetState()
  }

  async function deleteAllData() {
    await deleteAllModels()

View on GitHub (pinned to 438a067dde)