moeru-ai/airi · error · Error
The MAGIC Live2D driver is not playing.
Error message
The MAGIC Live2D driver is not playing.
What it means
`replace()` swaps the state generator of a driver that is currently animating. If no animation is running (the internal `generator` is undefined), there is nothing to replace and the driver throws instead of silently starting. Use `start()` to begin playback from idle.
Source
Thrown at packages/model-driver-magic-live2d/src/driver.ts:122
}
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()
}
function stop() {
if (!generator)
return
if (animationFrame !== undefined)
cancelFrame(animationFrame)
generator = undefined
animationFrame = undefined
accumulatedMs = 0
filter.reset()
outputFrame = undefined
options.onOutput?.(undefined)View on GitHub (pinned to 9c213115f8)
Solutions
- Call `driver.start(generator)` instead of `replace()` when the driver is idle.
- Track playing state and branch: `playing ? replace(g) : start(g)`.
- Ensure `start()` is invoked in the mount/lifecycle path before any `replace()` calls can fire.
- Check that a prior `stop()` (explicit or from an error handler) did not retire the driver unexpectedly.
Example fix
// before driver.replace(generator) // after if (driver.isPlaying()) driver.replace(generator) else driver.start(generator)
Defensive patterns
Strategy: validation
Validate before calling
function canReplace(driver: MagicLive2dDriver): boolean {
return driver.isPlaying()
} Try / catch
try {
driver.replace(generator)
} catch (error) {
if (error instanceof Error && error.message.includes('not playing')) {
driver.start(generator)
} else {
throw error
}
} Prevention
- Only call replace() after a confirmed start().
- Initialize the driver with start() in your mount path before any dynamic updates.
- Track lifecycle state (idle/playing/stopped) explicitly in app state.
- Avoid calling replace() from error handlers that may have already stopped the driver.
When it happens
Trigger: Calling `driver.replace(gen)` before any `start()` call, or after `stop()` has cleared the generator. E.g. calling `replace` during component setup before the animation loop was ever started.
Common situations: Calling replace in an init path that runs before start; calling replace after an error path already stopped the driver; race where a stop happened between user intent and replace.
Related errors
- The MAGIC Live2D driver is already 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/391fceb339e07804.
Report an issue: GitHub.