moeru-ai/airi · error · Error

The motion samples must be in time order.

Error message

The motion samples must be in time order.

What it means

Like the source samples of a motion project, recording samples must be non-decreasing in atMs so the interpolator can locate the surrounding pair for any time. parseLive2DMotionRecording throws this error when any sample's atMs is less than its predecessor's.

Source

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

  let input: unknown
  try {
    input = JSON.parse(raw)
  }
  catch {
    throw new Error('The file does not contain valid JSON.')
  }

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

  const { durationMs, samples } = result.output
  if (samples[0].atMs !== 0)
    throw new Error('The first motion sample must start at 0 ms.')

  for (let index = 1; index < samples.length; index++) {
    if (samples[index].atMs < samples[index - 1].atMs)
      throw new Error('The motion samples must be in time order.')
  }

  if (samples.at(-1)!.atMs > durationMs)
    throw new Error('A motion sample occurs after the recording duration.')

  return result.output
}

/**
 * Serializes a Live2D joystick recording as a readable JSON file.
 *
 * @example
 * stringifyLive2DMotionRecording({ format: 'airi-live2d-motion/v6', ... })
 * // => readable JSON ending with a newline
 */
export function stringifyLive2DMotionRecording(recording: ReadonlyLive2DMotionRecording): string {
  return `${JSON.stringify(recording, null, 2)}\n`
}

View on GitHub (pinned to 9c213115f8)

Solutions

  1. Sort samples by atMs ascending before parsing
  2. Fix the recording pipeline so samples are appended in chronological order
  3. Re-record the motion if the source data is genuinely corrupted

Example fix

// before
parseLive2DMotionRecording(raw)
// after
const data = JSON.parse(raw)
data.samples.sort((a, b) => a.atMs - b.atMs)
parseLive2DMotionRecording(JSON.stringify(data))
Defensive patterns

Strategy: validation

Validate before calling

const data = JSON.parse(raw)
data.samples.sort((a, b) => a.atMs - b.atMs)
raw = JSON.stringify(data)

Type guard

const isSortedByAtMs = (samples) => samples.every((s, i) => i === 0 || s.atMs >= samples[i - 1].atMs)

Try / catch

try {
  const recording = parseLive2DMotionRecording(raw)
}
catch (error) {
  if (error.message === 'The motion samples must be in time order.') {
    const data = JSON.parse(raw)
    data.samples.sort((a, b) => a.atMs - b.atMs)
    raw = JSON.stringify(data)
    return parseLive2DMotionRecording(raw)
  }
  throw error
}

Prevention

When it happens

Trigger: Calling parseLive2DMotionRecording on a recording whose samples array has a decreasing atMs somewhere after the first element, from out-of-order appends, merges, or edits.

Common situations: Merging multiple recordings or input streams without sorting; editor drag operations that move a sample earlier without reordering the array; hand-edited JSON.

Related errors


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