moeru-ai/airi · error · Error

The motion project contains an invalid overlay point.

Error message

The motion project contains an invalid overlay point.

What it means

parseLive2DMotionProject requires every overlay point's atMs to lie within the overlay's own span [startMs, endMs]. Points outside the span cannot be interpolated correctly and indicate corrupt or misplaced data. This error is thrown when any overlay point's atMs is less than startMs or greater than endMs.

Source

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

  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) {
    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. Clamp each point.atMs to [overlay.startMs, overlay.endMs] before parsing
  2. Recompute or move points whenever an overlay span changes
  3. Regenerate the overlay from its source data if points and span disagree

Example fix

// before
overlay.points = importedPoints
// after
overlay.points = importedPoints.map(p => ({ ...p, atMs: Math.min(Math.max(p.atMs, overlay.startMs), overlay.endMs) }))
Defensive patterns

Strategy: validation

Validate before calling

for (const overlay of project.overlays) {
  for (const point of overlay.points)
    point.atMs = Math.min(Math.max(point.atMs, overlay.startMs), overlay.endMs)
}

Type guard

const pointsWithinSpan = (o) => o.points.every(p => p.atMs >= o.startMs && p.atMs <= o.endMs)

Try / catch

try {
  parseLive2DMotionProject(project)
}
catch (error) {
  if (error.message.includes('invalid overlay point'))
    console.error('An overlay point lies outside its span; clamp point.atMs to [startMs, endMs]')
  throw error
}

Prevention

When it happens

Trigger: Calling parseLive2DMotionProject with an overlay whose points contain atMs values outside its [startMs, endMs] window, typically after editing the span or moving points independently.

Common situations: Narrowing an overlay's span after points were placed; drag-editing UIs that update points before spans; programmatically shifting overlay spans without re-clamping point timestamps; imported motion data with mismatched units.

Related errors


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