{"record":{"id":"8b705f370ee08bd4","repo":"moeru-ai/airi","slug":"the-file-is-not-an-airi-live2d-motion-recording","errorCode":null,"errorMessage":"The file is not an AIRI Live2D motion recording.","messagePattern":"The file is not an AIRI Live2D motion recording\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/features/devtools/motion/live2d/composables/recording.ts","lineNumber":92,"sourceCode":"/**\n * Parses and validates a Live2D joystick recording at the file boundary.\n *\n * @example\n * parseLive2DMotionRecording('{\"format\":\"airi-live2d-motion/v6\", ...}')\n * // => a validated recording\n */\nexport function parseLive2DMotionRecording(raw: string): Live2DMotionRecording {\n  let input: unknown\n  try {\n    input = JSON.parse(raw)\n  }\n  catch {\n    throw new Error('The file does not contain valid JSON.')\n  }\n\n  const result = safeParse(live2dMotionRecordingSchema, input)\n  if (!result.success)\n    throw new Error('The file is not an AIRI Live2D motion recording.')\n\n  const { durationMs, samples } = result.output\n  if (samples[0].atMs !== 0)\n    throw new Error('The first motion sample must start at 0 ms.')\n\n  for (let index = 1; index < samples.length; index++) {\n    if (samples[index].atMs < samples[index - 1].atMs)\n      throw new Error('The motion samples must be in time order.')\n  }\n\n  if (samples.at(-1)!.atMs > durationMs)\n    throw new Error('A motion sample occurs after the recording duration.')\n\n  return result.output\n}\n\n/**\n * Serializes a Live2D joystick recording as a readable JSON file.","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/moeru-ai/airi/blob/9c213115f8bd0fff9e6eabab02b077ac32da21be/packages/stage-ui/src/features/devtools/motion/live2d/composables/recording.ts#L74-L110","documentation":"After JSON parsing succeeds, parseLive2DMotionRecording validates the object against live2dMotionRecordingSchema with Valibot's safeParse. If the shape does not match the recording schema (wrong or missing fields, wrong types), the file is structurally not an AIRI Live2D motion recording and this error is thrown. It is deliberately generic so users know the file format itself is wrong, not just its content.","triggerScenarios":"Calling parseLive2DMotionRecording on JSON that parses but lacks the schema-required fields (durationMs, samples with atMs/values) or has wrong types, e.g. samples as an object instead of an array, missing version fields, or a different AIRI export format.","commonSituations":"Importing a motion project JSON instead of a recording JSON; importing a recording from an older/newer schema version; hand-crafted files with missing required properties; exporting from a fork with a divergent schema.","solutions":["Import a file produced by the AIRI Live2D motion recorder with the matching schema version","Compare the file's top-level keys against live2dMotionRecordingSchema and fix missing/mistyped fields","Upgrade/downgrade AIRI if the recording came from an incompatible version","Inspect result.issues from safeParse to identify the exact schema mismatch"],"exampleFix":"// before\n// { \"frames\": [...], \"length\": 3000 }\n// after\n// { \"durationMs\": 3000, \"samples\": [{ \"atMs\": 0, \"values\": { ... } }] }","handlingStrategy":"validation","validationCode":"import { safeParse } from 'valibot'\nimport { live2dMotionRecordingSchema } from './schema'\nconst precheck = safeParse(live2dMotionRecordingSchema, JSON.parse(raw))\nif (!precheck.success)\n  console.error('Schema issues:', precheck.issues.map(i => `${i.path?.map(p => p.key).join('.')} ${i.message}`))","typeGuard":"const isMotionRecording = (v) =>\n  typeof v === 'object' && v !== null\n  && typeof v.durationMs === 'number'\n  && Array.isArray(v.samples)\n  && v.samples.every(s => typeof s.atMs === 'number' && typeof s.values === 'object')","tryCatchPattern":"try {\n  const recording = parseLive2DMotionRecording(raw)\n}\ncatch (error) {\n  if (error.message === 'The file is not an AIRI Live2D motion recording.')\n    showImportError('This file is not an AIRI Live2D motion recording. Export one from the motion recorder.')\n  else throw error\n}","preventionTips":["Only import files produced by the AIRI motion recorder","Check schema version fields before upgrading/downgrading AIRI","Validate against live2dMotionRecordingSchema in tests for fixtures","Do not hand-edit recording JSON without running schema validation"],"tags":["validation","schema","valibot","file-import"],"backgroundTag":"schema-validation-failed","analyzedSha":"9c213115f8bd0fff9e6eabab02b077ac32da21be","analyzedAt":"2026-09-02T04:27:24.639Z","contentChangedAt":"2026-09-02T04:27:24.639Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}