moeru-ai/airi · warning
Emotion ${emotionName} not found
Error message
Emotion ${emotionName} not found What it means
useVRMEmote(vrm).setEmotion() looks up emotionName in a local emotionStates Map that defines exactly: happy, sad, angry, surprised, neutral, think, relaxed. Unknown names warn and return — no expression change, no transition. Note the VRM table is intentionally narrower than the shared Emotion enum (no curious/question/awkward), so valid upstream emotions can still miss here.
Source
Thrown at packages/stage-ui-three/src/composables/vrm/expression.ts:100
expression: [
{ name: 'relaxed', value: 0.7 },
],
blendDuration: 0.4,
}],
])
const clearResetTimeout = () => {
if (resetTimeout.value) {
clearTimeout(resetTimeout.value)
resetTimeout.value = undefined
}
}
const setEmotion = (emotionName: string, intensity = 1) => {
clearResetTimeout()
if (!emotionStates.has(emotionName)) {
console.warn(`Emotion ${emotionName} not found`)
return
}
const emotionState = emotionStates.get(emotionName)!
currentEmotion.value = emotionName
isTransitioning.value = true
transitionProgress.value = 0
// Store current expression values as starting point BEFORE resetting,
// so the lerp transition starts from the actual displayed values
// instead of snapping to 0 first (fixes #590).
currentExpressionValues.value.clear()
targetExpressionValues.value.clear()
const normalizedIntensity = clampIntensity(intensity)
if (vrm.expressionManager) {
// Capture current values for all expressions we'll be transitioningView on GitHub (pinned to 677329427f)
Solutions
- Map emotion names through EMOTION_VRMExpressionName_value (packages/stage-ui/src/constants/emotions.ts) before calling setEmotion.
- Add the missing state to the emotionStates Map in packages/stage-ui-three/src/composables/vrm/expression.ts.
- Normalize/validate names at the boundary (queues.ts normalizeEmotionName pattern) so only supported keys reach the renderer.
Example fix
// before emote.setEmotion(emotionName) // 'curious' warns: not in emotionStates // after const VRM_EMOTIONS = new Set(['happy', 'sad', 'angry', 'surprised', 'neutral', 'think', 'relaxed']) emote.setEmotion(VRM_EMOTIONS.has(emotionName) ? emotionName : 'neutral')
Defensive patterns
Strategy: type-guard
Validate before calling
const VRM_EMOTIONS = new Set(['happy', 'sad', 'angry', 'surprised', 'neutral', 'think', 'relaxed']) if (!VRM_EMOTIONS.has(name)) name = 'neutral'
Type guard
function isVRMEmotion(name: string): boolean {
return ['happy', 'sad', 'angry', 'surprised', 'neutral', 'think', 'relaxed'].includes(name)
} Prevention
- Expose the supported set from the emote composable instead of duplicating it at call sites.
- Map shared Emotion values through EMOTION_VRMExpressionName_value before calling setEmotion.
- Extend emotionStates and the shared enum together in one change.
When it happens
Trigger: Forwarding Emotion enum values like 'curious', 'question', or 'awkward' straight from the emotions queue to setEmotion; passing arbitrary LLM strings without normalization; typos ('surprise' instead of 'surprised').
Common situations: Shared emotion vocabulary grows but the VRM emote table is not extended; feeding raw ACT payload names into the renderer; a model lacking 'relaxed' custom expressions in a forked table.
Related errors
- [mmd] emotion ${emotion} not found
- VRM model loading failure!
- No VRM animations found in the .vrma file
- No VRM found
- No hips node found in VRM model.
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/9b33824d5389574b.
Report an issue: GitHub.