{"record":{"id":"564acd6f7ad23833","repo":"moeru-ai/airi","slug":"invalid-chat-session-export-format","errorCode":null,"errorMessage":"Invalid chat session export format","messagePattern":"Invalid chat session export format","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/composables/use-data-maintenance.ts","lineNumber":99,"sourceCode":"  function deleteAllChatSessions() {\n    chatOrchestrator.cancelPendingSends()\n    chatStore.resetAllSessions()\n  }\n\n  async function exportChatSessions() {\n    const data = await chatStore.exportSessions()\n    return new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })\n  }\n\n  function isChatSessionsPayload(payload: unknown): payload is ChatSessionsExport {\n    if (!payload || typeof payload !== 'object')\n      return false\n    return (payload as { format?: string }).format === 'chat-sessions-index:v1'\n  }\n\n  async function importChatSessions(payload: Record<string, unknown>) {\n    if (!isChatSessionsPayload(payload))\n      throw new Error('Invalid chat session export format')\n    await chatStore.importSessions(payload)\n    return payload\n  }\n\n  async function resetSettingsState() {\n    await settingsStore.resetState()\n    audioSettingsStore.resetState()\n    live2dParamsStore.resetState()\n    live2dSettingsStore.resetState()\n    live2dMagicSettingsStore.resetState()\n    threeStore.resetModelStore()\n    mcpStore.resetState()\n    onboardingStore.resetSetupState()\n    airiCardStore.resetState()\n  }\n\n  async function deleteAllData() {\n    await deleteAllModels()","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/stage-ui/src/composables/use-data-maintenance.ts#L81-L117","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-export chat sessions with the same app version's export button and import that file","Open the JSON and confirm the top level contains the key format set to 'chat-sessions-index:v1'","If migrating an older export, write a one-off converter that sets the v1 format key and the expected sessions structure","Validate the file is complete, parseable JSON before import — truncated downloads fail the check"],"exampleFix":"// before\nconst payload = JSON.parse(await file.text())\nawait importChatSessions(payload) // throws: Invalid chat session export format\n\n// after\nconst payload = JSON.parse(await file.text())\nif (payload?.format !== 'chat-sessions-index:v1')\n  throw new Error('Selected file is not an AIRI chat export (expected chat-sessions-index:v1)')\nawait importChatSessions(payload)","handlingStrategy":"type-guard","validationCode":"const raw = JSON.parse(await file.text())\nconst isExport = !!raw && typeof raw === 'object' && raw.format === 'chat-sessions-index:v1'\nif (!isExport)\n  throw new Error('Not a chat sessions export')","typeGuard":"function isChatSessionsPayload(payload: unknown): payload is ChatSessionsExport {\n  return !!payload && typeof payload === 'object'\n    && (payload as { format?: string }).format === 'chat-sessions-index:v1'\n}","tryCatchPattern":"try {\n  await importChatSessions(payload)\n}\ncatch (err) {\n  // Distinguish format rejection from store failures so the UI tells the\n  // user to re-check the selected file instead of reporting a data bug.\n  toast(err instanceof Error && err.message === 'Invalid chat session export format'\n    ? 'Selected file is not a chat session export'\n    : 'Import failed')\n}","preventionTips":["Export and import with the same app version so format markers match","Show the expected format string in the import UI next to the file picker","Parse and format-check the file before calling importSessions","Never hand-edit exports; write a converter that emits the exact v1 shape"],"tags":["import-export","chat-sessions","validation","json"],"backgroundTag":"schema-validation-failed","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}