moeru-ai/airi · error · Error
A motion sample occurs after the recording duration.
Error message
A motion sample occurs after the recording duration.
What it means
A recording's samples must all occur within the declared durationMs; anything beyond the duration can never be played and indicates inconsistent metadata. parseLive2DMotionRecording compares the last sample's atMs against durationMs and throws this error if the sample exceeds it.
Source
Thrown at packages/stage-ui/src/features/devtools/motion/live2d/composables/recording.ts:104
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`
}
/** Owns one in-memory Live2D motion recording and its playback lifecycle. */
export function useLive2DMotionRecording(
options: UseLive2DMotionRecordingOptions,View on GitHub (pinned to 9c213115f8)
Solutions
- Increase durationMs to at least the last sample's atMs
- Remove samples whose atMs exceed durationMs
- Fix the recorder to finalize durationMs after all samples are flushed
Example fix
// before parseLive2DMotionRecording(raw) // after const data = JSON.parse(raw) data.durationMs = Math.max(data.durationMs, data.samples.at(-1).atMs) parseLive2DMotionRecording(JSON.stringify(data))
Defensive patterns
Strategy: validation
Validate before calling
const data = JSON.parse(raw) const last = data.samples?.at(-1)?.atMs ?? 0 data.durationMs = Math.max(data.durationMs ?? 0, last) raw = JSON.stringify(data)
Type guard
const withinDuration = (v) => Array.isArray(v.samples) && (v.samples.length === 0 || v.samples.at(-1).atMs <= v.durationMs)
Try / catch
try {
const recording = parseLive2DMotionRecording(raw)
}
catch (error) {
if (error.message === 'A motion sample occurs after the recording duration.')
showImportError('A sample lies beyond durationMs; fix durationMs or trim samples.')
else throw error
} Prevention
- Update durationMs whenever samples are appended
- Finalize durationMs after flushing all recorded samples
- Validate duration against last sample in export tooling
When it happens
Trigger: Calling parseLive2DMotionRecording where samples.at(-1).atMs > durationMs, e.g. durationMs was manually shortened after appending samples, or samples were appended after recording stopped.
Common situations: Extending a recording by appending samples without updating durationMs; editing durationMs in the JSON by hand; a recorder bug that flushes buffered samples after the stop time was computed.
Related errors
- The first motion sample must start at 0 ms.
- The crop range is outside the motion timeline.
- The motion project source timeline is invalid.
- The motion project source samples are not in time order.
- The motion project contains an invalid overlay span.
AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02).
Data as JSON: /api/errors/3fbd9728ebdbc228.
Report an issue: GitHub.