moeru-ai/airi · warning
Error removing old model:
Error message
Error removing old model:
What it means
While swapping Live2D models, removing the old model from the PIXI stage and destroying it threw. The expression controller was already disposed and internalModelRef cleared; after the catch, model.value is undefined and loading of the new model proceeds. If removeChild failed before destroy, stale display objects may linger on the stage.
Source
Thrown at packages/stage-ui-live2d/src/components/scenes/live2d/Model.vue:235
catch {
modelLoading.value = false
componentState.value = 'mounted'
return
}
}
// REVIEW: here as await until(...) guarded the pixiApp and stage to be valid.
if (model.value && pixiApp.value?.stage) {
// Dispose expression controller before destroying the old model
expressionController.dispose()
internalModelRef.value = undefined
try {
pixiApp.value.stage.removeChild(model.value)
model.value.destroy()
}
catch (error) {
console.warn('Error removing old model:', error)
}
model.value = undefined
}
if (!modelSrcRef.value) {
console.warn('No Live2D model source provided.')
modelLoading.value = false
componentState.value = 'mounted'
return
}
try {
if (isUnmounted) {
modelLoading.value = false
componentState.value = 'mounted'
return
}
const live2DModel = new Live2DModel<PixiLive2DInternalModel>()View on GitHub (pinned to 677329427f)
Solutions
- Proceed — the new model load continues after the warning
- Serialize swaps behind a mutex like loadModel's modelLoadMutex to prevent overlapping destroys
- Keep the source guard: only dispose when model.value and pixiApp.value?.stage both exist
- After a context loss, recreate the PIXI app instead of reusing a half-destroyed stage
Defensive patterns
Strategy: try-catch
Validate before calling
if (!model.value || !pixiApp.value?.stage) return // skip dispose when the app was torn down (source guard)
Try / catch
try {
pixiApp.value.stage.removeChild(model.value)
model.value.destroy()
}
catch (error) {
console.warn('Error removing old model:', error)
}
model.value = undefined // proceed with the new model regardless (source pattern) Prevention
- Serialize model swaps behind a mutex like loadModel's modelLoadMutex to avoid racing destroys
- Dispose the expression controller before destroying the model (source ordering)
- After a WebGL context loss, recreate the PIXI app instead of reusing a half-destroyed stage
When it happens
Trigger: Switching models rapidly so a previous destroy overlaps the next load; WebGL context lost before destroy; destroying a model whose textures were already freed.
Common situations: User rapidly switches between Live2D models in settings; device suspends and resumes with a lost context; HMR reload during a model swap.
Related errors
- [Model.vue] Expression controller initialization failed:
- [Model.vue] Expression controller initialisation failed:
- [OPFS] ZipLoader not found in middlewares, caching disabled
- No Live2D model source provided.
- Cannot set motion: model not loaded
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/c5b654a9571790ec.
Report an issue: GitHub.