moeru-ai/airi · info
[mmd] gaze: no eye bone matched (左目/右目/両目); eye tracking dis
Error message
[mmd] gaze: no eye bone matched (左目/右目/両目); eye tracking disabled. Bones containing 目/eye:
What it means
createGazeController() in packages/stage-ui-mmd/src/composables/mmd/gaze.ts builds bone-based eye tracking by looking up standard MMD eye bones: 左目/eye_L/EyeLeft/LeftEye and 右目/eye_R/EyeRight/RightEye, with 両目/'Eyes' as a single-bone fallback. If none match, it logs every bone whose name contains 目 or eye (to help identify renames) and simply runs with zero eye bones: head-only gaze and idle saccades continue, eye rotation is disabled.
Source
Thrown at packages/stage-ui-mmd/src/composables/mmd/gaze.ts:81
*/
export function createGazeController(mesh: SkinnedMesh): GazeController {
const eyeBones: GazeBone[] = []
const left = findBone(mesh, LEFT_EYE_NAMES)
const right = findBone(mesh, RIGHT_EYE_NAMES)
if (left)
eyeBones.push({ bone: left, rest: left.quaternion.clone() })
if (right)
eyeBones.push({ bone: right, rest: right.quaternion.clone() })
// Fallback: a single both-eyes bone the eyeballs are skinned to directly.
if (eyeBones.length === 0) {
const both = findBone(mesh, BOTH_EYES_NAMES)
if (both)
eyeBones.push({ bone: both, rest: both.quaternion.clone() })
}
if (eyeBones.length === 0) {
console.warn(
'[mmd] gaze: no eye bone matched (左目/右目/両目); eye tracking disabled. '
+ 'Bones containing 目/eye:',
mesh.skeleton.bones.filter(b => /目|eye/i.test(b.name)).map(b => b.name),
)
}
const headBoneRaw = findBone(mesh, HEAD_NAMES)
const headBone: GazeBone | undefined = headBoneRaw
? { bone: headBoneRaw, rest: headBoneRaw.quaternion.clone() }
: undefined
// Smoothed current aim, lerped toward the target each frame.
const current: GazeOffset = { x: 0, y: 0 }
const scratchEuler = new Euler()
const scratchQuat = new Quaternion()
// Idle saccade state.
let saccade: GazeOffset = { x: 0, y: 0 }View on GitHub (pinned to 677329427f)
Solutions
- Read the warn's printed bone list; if an eye bone exists under a different name, rename it to 左目/右目 in a PMX editor (PMXE/Blender+mmd_tools).
- Or extend LEFT_EYE_NAMES/RIGHT_EYE_NAMES/BOTH_EYE_NAMES in gaze.ts to cover your model's naming.
- If eyes are morph-driven, accept the degradation (head tracking still works) — no code change needed.
Example fix
// in packages/stage-ui-mmd/src/composables/mmd/gaze.ts // before const LEFT_EYE_NAMES = ['左目', 'eye_L', 'EyeLeft', 'LeftEye'] // after (cover your model's naming) const LEFT_EYE_NAMES = ['左目', 'eye_L', 'EyeLeft', 'LeftEye', 'eyeInner_L', 'EYE.L']
Defensive patterns
Strategy: validation
Validate before calling
const hasEyeBones = mesh.skeleton.bones.some(b => ['左目', 'eye_L', 'EyeLeft', 'LeftEye'].includes(b.name))
|| mesh.skeleton.bones.some(b => ['右目', 'eye_R', 'EyeRight', 'RightEye'].includes(b.name))
|| mesh.skeleton.bones.some(b => ['両目', 'Eyes'].includes(b.name))
if (!hasEyeBones) console.info('gaze: head-only tracking for this model') Type guard
function modelSupportsEyeGaze(mesh: SkinnedMesh): boolean {
const names = new Set(mesh.skeleton.bones.map(b => b.name))
return ['左目', 'eye_L', 'EyeLeft', 'LeftEye', '右目', 'eye_R', 'EyeRight', 'RightEye', '両目', 'Eyes'].some(n => names.has(n))
} Prevention
- Validate PMX assets in onboarding: warn users when standard eye bones are absent.
- Keep the bone-name candidate lists in gaze.ts updated as you encounter new export conventions.
- Treat eye tracking as optional; design UX so head-only gaze is acceptable.
When it happens
Trigger: Loading a PMX whose eye bones use non-standard names (e.g. 'eyeLeft001', 'EYE.L'), whose eyeballs are skinned directly to the head bone, or whose eyes are driven only by morphs instead of bones.
Common situations: Western-authored or blender-exported MMD-style models that translate standard Japanese bone names; heavily modified models; models rigged for morph-only blinking where eye bones were deleted.
Related errors
- MMD ZIP must contain a .pmx or .pmd model file
- [mmd] motion "${descriptor.name}" loaded but has 0 tracks ma
- [mmd] playAction skipped: "${name}" is not registered
- [mmd] setIdleMotion skipped: "${name}" is not registered
- [mmd] emotion ${emotion} not found
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/80d8c88eb205d683.
Report an issue: GitHub.