moeru-ai/airi · error · Error

The motion project overlay points are not in time order.

Error message

The motion project overlay points are not in time order.

What it means

parseLive2DMotionProject requires overlay points to be sorted by atMs in non-decreasing order, matching the same invariant enforced on source samples. This error is thrown when consecutive overlay points have decreasing atMs values.

Source

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

  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) {
    if (overlay.endMs > project.durationMs || overlay.startMs > overlay.endMs)
      throw new Error('The motion project contains an invalid overlay span.')
    if (overlay.points.some(point => point.atMs < overlay.startMs || point.atMs > overlay.endMs))
      throw new Error('The motion project contains an invalid overlay point.')
    for (let index = 1; index < overlay.points.length; index++) {
      if (overlay.points[index].atMs < overlay.points[index - 1].atMs)
        throw new Error('The motion project overlay points are not in time order.')
    }
  }
  return structuredClone(project)
}

View on GitHub (pinned to 9c213115f8)

Solutions

  1. Sort overlay.points by atMs ascending before calling parseLive2DMotionProject
  2. Fix the overlay editing code to keep points ordered on insert/move
  3. Reject or re-sort imported point data during ingest

Example fix

// before
parseLive2DMotionProject(project)
// after
project.overlays.forEach(o => o.points.sort((a, b) => a.atMs - b.atMs))
parseLive2DMotionProject(project)
Defensive patterns

Strategy: validation

Validate before calling

project.overlays.forEach(o => o.points.sort((a, b) => a.atMs - b.atMs))

Type guard

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

Try / catch

try {
  parseLive2DMotionProject(project)
}
catch (error) {
  if (error.message.includes('overlay points are not in time order')) {
    project.overlays.forEach(o => o.points.sort((a, b) => a.atMs - b.atMs))
    parseLive2DMotionProject(project)
  }
  else throw error
}

Prevention

When it happens

Trigger: Calling parseLive2DMotionProject where an overlay's points array has atMs[i] < atMs[i-1], e.g. points inserted at the front, interleaved from multiple sources, or manually reordered.

Common situations: UI drag interactions that move a point earlier than its predecessor; merging keyframes from two recording passes; hand-edited JSON with reordered entries.

Related errors


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