{"record":{"id":"a9753bfb2aa56b53","repo":"moeru-ai/airi","slug":"mmd-emotion-emotion-not-found","errorCode":null,"errorMessage":"[mmd] emotion ${emotion} not found","messagePattern":"\\[mmd\\] emotion (.+?) not found","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui-mmd/src/composables/mmd/expression.ts","lineNumber":56,"sourceCode":"  const isTransitioning = ref(false)\n  const transitionProgress = ref(0)\n  const startWeights = new Map<MorphSlot, number>()\n  const targetWeights = new Map<MorphSlot, number>()\n  let resetTimeout: ReturnType<typeof setTimeout> | undefined\n\n  function clearResetTimeout() {\n    if (resetTimeout) {\n      clearTimeout(resetTimeout)\n      resetTimeout = undefined\n    }\n  }\n\n  function setEmotion(emotion: Emotion, intensity = 1) {\n    clearResetTimeout()\n\n    const state = EMOTION_MORPHS[emotion]\n    if (!state) {\n      console.warn(`[mmd] emotion ${emotion} not found`)\n      return\n    }\n\n    currentEmotion.value = emotion\n    isTransitioning.value = true\n    transitionProgress.value = 0\n    startWeights.clear()\n    targetWeights.clear()\n\n    const normalized = clampIntensity(intensity)\n\n    // Capture where each expression slot currently sits so the cross-fade\n    // starts from the live value instead of snapping to zero first.\n    for (const slot of EXPRESSION_SLOTS) {\n      startWeights.set(slot, morphs.get(slot))\n      targetWeights.set(slot, 0)\n    }\n","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui-mmd/src/composables/mmd/expression.ts#L38-L74","documentation":"useMMDEmote().setEmotion() maps an emotion name to MMD morph influences via the EMOTION_MORPHS table (packages/stage-ui-mmd/src/constants/morphs.ts). When the passed Emotion key has no entry in that table, the function warns and returns without starting a transition, so the model's face stays in its previous state. This is a graceful-degradation guard, not a crash: TypeScript normally prevents bad keys, but the value often arrives from untrusted LLM output at runtime.","triggerScenarios":"An ACT emotion payload from the agent/LLM resolves to an Emotion enum value (e.g. 'curious', 'question', 'awkward') that has no morph mapping in EMOTION_MORPHS for the loaded MMD model, and the emotions queue forwards it to useMMDEmote().setEmotion(). Calling setEmotion('relaxed' as Emotion) directly with a key outside the table also triggers it.","commonSituations":"Adding a new value to the shared Emotion enum (packages/stage-ui/src/constants/emotions.ts) without adding a matching EMOTION_MORPHS entry; MMD models whose morph sets only cover a subset of emotions; casting a raw string to Emotion to bypass the compiler.","solutions":["Check which Emotion keys EMOTION_MORPHS actually defines in packages/stage-ui-mmd/src/constants/morphs.ts and confirm the emotion you send is among them.","Filter/normalize emotion names at the boundary (as queues.ts normalizeEmotionName does) so only mapped emotions reach setEmotion.","Add an EMOTION_MORPHS entry mapping the missing emotion to available morph slots (smile/anger/sad/surprise/troubled/serious).","Fall back to a close mapped emotion (e.g. 'neutral') before calling setEmotion."],"exampleFix":"// before\nemote.setEmotion(rawName as Emotion) // warns when unmapped\n\n// after\nimport { EMOTION_MORPHS } from '../../constants/morphs'\nif (rawName in EMOTION_MORPHS) {\n  emote.setEmotion(rawName as Emotion, intensity)\n}\nelse {\n  emote.setEmotion(Emotion.Neutral, intensity)\n}","handlingStrategy":"type-guard","validationCode":"import { EMOTION_MORPHS } from '../constants/morphs'\nconst supported = Object.keys(EMOTION_MORPHS)\nif (!supported.includes(name)) name = 'neutral'","typeGuard":"function isMappedMmdEmotion(name: string): name is Emotion {\n  return Object.prototype.hasOwnProperty.call(EMOTION_MORPHS, name)\n}","tryCatchPattern":null,"preventionTips":["Never cast arbitrary strings to Emotion; normalize with a whitelist first (see normalizeEmotionName in queues.ts).","When adding an Emotion enum value, add the EMOTION_MORPHS entry in the same change.","Centralize emotion vocabulary in packages/stage-ui/src/constants/emotions.ts so renderers and queues share one list."],"tags":["mmd","emotion","morphs","expression","llm-output"],"backgroundTag":"unknown-emotion-name","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}