moeru-ai/airi · error · Error

The file does not contain valid JSON.

Error message

The file does not contain valid JSON.

What it means

parseLive2DMotionProject first attempts JSON.parse on the raw file contents. If the string is not syntactically valid JSON, it throws this error instead of leaking the native SyntaxError, signaling that the imported file is not parseable JSON at all. Validation of the parsed shape happens afterwards with motionProjectSchema.

Source

Thrown at packages/stage-ui/src/features/devtools/motion/live2d/composables/keyframes.ts:447

): Live2DMotionKeyframe[] {
  return points
    .map(point => point.id === id ? { ...point, atMs, value } : point)
    .sort((left, right) => left.atMs - right.atMs)
}

/** Serializes a motion project with its source recording and overlays. */
export function stringifyLive2DMotionProject(project: Live2DMotionProject): string {
  return `${JSON.stringify(project, null, 2)}\n`
}

/** Parses a motion project file and checks its structural and timeline invariants. */
export function parseLive2DMotionProject(raw: string): Live2DMotionProject {
  let input: unknown
  try {
    input = JSON.parse(raw)
  }
  catch {
    throw new Error('The file does not contain valid JSON.')
  }

  const result = safeParse(motionProjectSchema, input)
  if (!result.success)
    throw new Error('The file is not an AIRI Live2D motion project.')

  const project = result.output
  if (project.source.durationMs !== project.durationMs)
    throw new Error('The motion project source is invalid.')

  if (project.source.samples[0].atMs !== 0 || project.source.samples.at(-1)!.atMs > project.durationMs)
    throw new Error('The motion project source timeline is invalid.')
  for (let index = 1; index < project.source.samples.length; index++) {
    if (project.source.samples[index].atMs < project.source.samples[index - 1].atMs)
      throw new Error('The motion project source samples are not in time order.')
  }

  for (const overlay of project.overlays) {

View on GitHub (pinned to 9c213115f8)

Solutions

  1. Validate the file content with JSON.parse in a try/catch or a JSON linter to find the syntax error location.
  2. Check the file is not empty or truncated; re-export the motion project.
  3. Ensure the file is read as UTF-8 text (e.g. File.text() or readFileSync(path, 'utf8')) and strip any BOM.
  4. Confirm the user selected the correct .json motion project file, not an asset or archive.

Example fix

// before
const raw = await file.arrayBuffer().then(b => String(b)) // binary garbage
parseLive2DMotionProject(raw)
// after
const raw = await file.text()
try {
  const project = parseLive2DMotionProject(raw)
}
catch (error) {
  if (error.message === 'The file does not contain valid JSON.')
    showError('Selected file is not JSON. Please choose an exported .json motion project.')
}
Defensive patterns

Strategy: validation

Validate before calling

function isParseableJson(raw) {
  if (typeof raw !== 'string' || raw.trim() === '')
    return false
  try {
    JSON.parse(raw)
    return true
  }
  catch {
    return false
  }
}

Try / catch

try {
  const project = parseLive2DMotionProject(raw)
}
catch (error) {
  switch (error.message) {
    case 'The file does not contain valid JSON.':
      notifyUser('This file is not valid JSON. Please select an exported AIRI motion project (.json).')
      break
    default: throw error
  }
}

Prevention

When it happens

Trigger: Calling parseLive2DMotionProject(raw) where raw is an empty string, truncated file content, a binary file, a file with a BOM or trailing garbage, or content in another format (YAML, XML) that is not valid JSON.

Common situations: User picks the wrong file in an import dialog (e.g. a .zip or .png); the project file was saved half-written due to a crash; file was edited by hand and syntax broke; reading a file with encoding issues introducing invalid characters.

Related errors


AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02). Data as JSON: /api/errors/dda863e122a6e703. Report an issue: GitHub.