moeru-ai/airi · error · Error
The motion project source samples are not in time order.
Error message
The motion project source samples are not in time order.
What it means
parseLive2DMotionProject validates a Live2D motion project before use. Source samples must be sorted by atMs in non-decreasing order because the interpolator walks samples forward and binary-searches by time; unsorted samples produce broken animation. This error is thrown when any sample's atMs is less than the previous sample's atMs.
Source
Thrown at packages/stage-ui/src/features/devtools/motion/live2d/composables/keyframes.ts:462
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) {
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
- Sort project.source.samples by atMs ascending before calling parseLive2DMotionProject
- Ensure the sample recording/collection code appends samples in chronological order
- Validate the JSON file for out-of-order timestamps before loading it
Example fix
// before parseLive2DMotionProject(project) // after project.source.samples.sort((a, b) => a.atMs - b.atMs) parseLive2DMotionProject(project)
Defensive patterns
Strategy: validation
Validate before calling
function samplesAreSorted(samples) {
return samples.every((s, i) => i === 0 || s.atMs >= samples[i - 1].atMs)
}
if (!samplesAreSorted(project.source.samples))
project.source.samples.sort((a, b) => a.atMs - b.atMs) Type guard
const isSortedByAtMs = (items) => items.every((s, i) => i === 0 || s.atMs >= items[i - 1].atMs)
Try / catch
try {
parseLive2DMotionProject(project)
}
catch (error) {
if (error.message.includes('not in time order')) {
project.source.samples.sort((a, b) => a.atMs - b.atMs)
parseLive2DMotionProject(project)
}
else throw error
} Prevention
- Always sort samples by atMs right before parsing
- Never append samples from async sources without ordering guarantees
- Add a unit test asserting sample order for every project fixture
When it happens
Trigger: Calling parseLive2DMotionProject with project.source.samples whose atMs values are not non-decreasing (e.g. samples appended out of order, records from parallel sources merged without sorting, or negative/zero timestamps inserted mid-array).
Common situations: Hand-edited or externally generated motion JSON where sample entries were reordered; merging recordings from multiple input devices without a stable sort; constructing samples programmatically with async callbacks that resolve out of order.
Related errors
- The motion project overlay points are not in time order.
- The motion samples must be in time order.
- The crop range is outside the motion timeline.
- The motion project contains an invalid overlay span.
- The motion project contains an invalid overlay point.
AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02).
Data as JSON: /api/errors/78a10b9838b28b6d.
Report an issue: GitHub.