{"record":{"id":"4ab69dc2c853713a","repo":"moeru-ai/airi","slug":"the-file-is-not-an-airi-live2d-motion-project","errorCode":null,"errorMessage":"The file is not an AIRI Live2D motion project.","messagePattern":"The file is not an AIRI Live2D motion project\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/features/devtools/motion/live2d/composables/keyframes.ts","lineNumber":452,"sourceCode":"\n/** Serializes a motion project with its source recording and overlays. */\nexport function stringifyLive2DMotionProject(project: Live2DMotionProject): string {\n  return `${JSON.stringify(project, null, 2)}\\n`\n}\n\n/** Parses a motion project file and checks its structural and timeline invariants. */\nexport function parseLive2DMotionProject(raw: string): Live2DMotionProject {\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(motionProjectSchema, input)\n  if (!result.success)\n    throw new Error('The file is not an AIRI Live2D motion project.')\n\n  const project = result.output\n  if (project.source.durationMs !== project.durationMs)\n    throw new Error('The motion project source is invalid.')\n\n  if (project.source.samples[0].atMs !== 0 || project.source.samples.at(-1)!.atMs > project.durationMs)\n    throw new Error('The motion project source timeline is invalid.')\n  for (let index = 1; index < project.source.samples.length; index++) {\n    if (project.source.samples[index].atMs < project.source.samples[index - 1].atMs)\n      throw new Error('The motion project source samples are not in time order.')\n  }\n\n  for (const overlay of project.overlays) {\n    if (overlay.endMs > project.durationMs || overlay.startMs > overlay.endMs)\n      throw new Error('The motion project contains an invalid overlay span.')\n    if (overlay.points.some(point => point.atMs < overlay.startMs || point.atMs > overlay.endMs))\n      throw new Error('The motion project contains an invalid overlay point.')\n    for (let index = 1; index < overlay.points.length; index++) {","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/moeru-ai/airi/blob/9c213115f8bd0fff9e6eabab02b077ac32da21be/packages/stage-ui/src/features/devtools/motion/live2d/composables/keyframes.ts#L434-L470","documentation":"After JSON parsing succeeds, parseLive2DMotionProject validates the parsed object against motionProjectSchema with Valibot's safeParse. This error is thrown when the structure does not match the expected AIRI Live2D motion project schema — valid JSON, but wrong shape, missing fields, or wrong types.","triggerScenarios":"Calling parseLive2DMotionProject with JSON that parses but lacks required motionProjectSchema fields (e.g. missing source.samples, durationMs as a string, overlays not an array) or contains unknown/incompatible shapes from an older or different tool.","commonSituations":"Importing a motion file exported by another application or an older AIRI schema version; hand-edited JSON with renamed or deleted fields; JSON that is a valid object of a different kind (e.g. a generic keyframe config).","solutions":["Compare the file against the current motionProjectSchema fields (source.durationMs, source.samples with atMs/values, durationMs, overlays) and fix missing/mistyped fields.","Re-export the project with the current version of the AIRI motion devtools to regenerate a schema-compliant file.","Check for schema version drift between the exporting tool and this code; migrate the file to the current schema.","Log result.issues from safeParse(motionProjectSchema, input) locally during debugging to see exactly which fields fail."],"exampleFix":"// before\nparseLive2DMotionProject('{ \"frames\": [...] }') // wrong shape\n// after\nparseLive2DMotionProject(JSON.stringify({\n  durationMs: 1000,\n  source: { durationMs: 1000, samples: [{ atMs: 0, values: {} }] },\n  overlays: []\n}))","handlingStrategy":"validation","validationCode":"import { safeParse } from 'valibot'\nimport { motionProjectSchema } from './keyframes' // wherever exported\n\nfunction isMotionProjectLike(raw) {\n  try {\n    return safeParse(motionProjectSchema, JSON.parse(raw)).success\n  }\n  catch {\n    return false\n  }\n}","typeGuard":"function isMotionProject(value) {\n  return safeParse(motionProjectSchema, value).success\n}","tryCatchPattern":"try {\n  const project = parseLive2DMotionProject(raw)\n}\ncatch (error) {\n  if (error.message === 'The file is not an AIRI Live2D motion project.') {\n    notifyUser('This JSON is not an AIRI Live2D motion project. Check the exporter version.')\n  }\n  else throw error\n}","preventionTips":["Validate files against motionProjectSchema at export time and at import time.","Version-tag exported project files so schema migrations can be detected.","Never hand-edit project JSON; re-export from the devtools instead.","Keep the exporting tool and consuming code on the same schema version."],"tags":["schema-validation","valibot","live2d","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"}