moeru-ai/airi · error · Error
The MAGIC Live2D driver is already playing.
Error message
The MAGIC Live2D driver is already playing.
What it means
The MAGIC Live2D driver's `start()` only accepts a new generator when idle. If a previous `start()` was called without a matching `stop()`, the internal `generator` slot is occupied and calling `start()` again throws. It guards against clobbering an active animation loop mid-generation.
Source
Thrown at packages/model-driver-magic-live2d/src/driver.ts:108
const frameIntervalMs = 1000 / generator.sampleRateHz
accumulatedMs += Math.min(250, Math.max(0, timestamp - lastFrameAt))
lastFrameAt = timestamp
let nextPose: Pose | undefined
while (accumulatedMs >= frameIntervalMs) {
nextPose = generatePose()
accumulatedMs -= frameIntervalMs
}
if (nextPose)
applyPose(nextPose)
animationFrame = requestFrame(generationFrame)
}
function start(nextGenerator: Generator<TState>) {
if (generator)
throw new Error('The MAGIC Live2D driver is already playing.')
generator = nextGenerator
accumulatedMs = 0
lastFrameAt = now()
filter.reset()
outputFrame = undefined
options.onOutput?.(undefined)
applyPose(generatePose())
animationFrame = requestFrame(generationFrame)
}
function replace(nextGenerator: Generator<TState>) {
if (!generator)
throw new Error('The MAGIC Live2D driver is not playing.')
generator = nextGenerator
accumulatedMs = 0
lastFrameAt = now()View on GitHub (pinned to 9c213115f8)
Solutions
- Call `driver.stop()` before `driver.start(newGenerator)` when the driver may already be playing.
- Check for a playing/generator state (or track it in your own code) and skip the redundant `start()`.
- Use `replace(nextGenerator)` instead of `start()` when you intend to swap generators in-place on a playing driver.
- Guard against duplicate component mounts by aborting/stopping in `onUnmounted`/cleanup.
Example fix
// before driver.start(newGenerator) // after if (driver.isPlaying()) driver.replace(newGenerator) else driver.start(newGenerator)
Defensive patterns
Strategy: try-catch
Validate before calling
function canStart(driver: MagicLive2dDriver): boolean {
return !driver.isPlaying()
} Try / catch
try {
driver.start(generator)
} catch (error) {
if (error instanceof Error && error.message.includes('already playing')) {
driver.replace(generator)
} else {
throw error
}
} Prevention
- Always pair start() with stop() in component lifecycle (onMounted/onUnmounted).
- Track playing state in your own store before calling start().
- Prefer replace() when swapping generators on a live driver.
- Guard re-entrant event handlers with an idempotency flag.
When it happens
Trigger: Calling `driver.start(gen)` while the driver is already playing (i.e. after a previous `start()` with no `stop()` in between). Common when wiring a second MAGIC session or re-entrant event handler that calls `start` on each event instead of on lifecycle transitions.
Common situations: Double-mounting a Vue component so two `onMounted` handlers both call start; a reconnect or model-reload path calling start without stopping the old driver first; a button that triggers start on every click.
Related errors
- The MAGIC Live2D driver is not playing.
- MediaPipe backend not initialized (call init() first)
- gameletKit requires a host gamelet orchestration runtime.
- Extension module `${input.id}` is already registered for ses
- Unknown extension session: ${sessionId}
AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02).
Data as JSON: /api/errors/20f101b530bacab9.
Report an issue: GitHub.