{"record":{"id":"6677f695f2a20da5","repo":"moeru-ai/airi","slug":"textures-not-found","errorCode":null,"errorMessage":"Textures not found","messagePattern":"Textures not found","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts","lineNumber":176,"sourceCode":"}\n\n// copy and modified from https://github.com/guansss/live2d-viewer-web/blob/f6060b2ce52c2e26b6b61fa903c837fe343f72d1/src/app/upload.ts#L81-L142\nfunction createFakeSettings(files: string[]): ModelSettings {\n  const mocFiles = files.filter(file => isMocFile(file))\n\n  if (mocFiles.length !== 1) {\n    const fileList = mocFiles.length ? `(${mocFiles.map(f => `\"${f}\"`).join(',')})` : ''\n\n    throw new Error(`Expected exactly one moc file, got ${mocFiles.length} ${fileList}`)\n  }\n\n  const mocFile = mocFiles[0]\n  const modelName = basename(mocFile).replace(/\\.moc3?/, '')\n\n  const textures = files.filter(f => f.endsWith('.png'))\n\n  if (!textures.length) {\n    throw new Error('Textures not found')\n  }\n\n  const motions = files.filter(f => f.endsWith('.mtn') || f.endsWith('.motion3.json'))\n  const physics = files.find(f => f.includes('physics'))\n  const pose = files.find(f => f.includes('pose'))\n\n  const settings = new Cubism4ModelSettings({\n    url: `${modelName}.model3.json`,\n    Version: 3,\n    FileReferences: {\n      Moc: mocFile,\n      Textures: textures,\n      Physics: physics,\n      Pose: pose,\n      Motions: motions.length\n        ? {\n            '': motions.map(motion => ({ File: motion })),\n          }","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/moeru-ai/airi/blob/9c213115f8bd0fff9e6eabab02b077ac32da21be/packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts#L158-L194","documentation":"Thrown by createFakeSettings when the uploaded files contain no .png entries. After pinning the single moc file, the synthesized Cubism4ModelSettings needs a Textures array; the heuristic simply collects all files ending in '.png'. An archive whose textures are .webp/.jpg, or where textures are missing entirely, fails here.","triggerScenarios":"Uploading a ZIP whose textures were converted to webp/jpg (breaking the .endsWith('.png') filter); textures inside a nested zip so their paths never appear in the flat file list; an archive with the moc but textures accidentally excluded; folder upload filtered to only some file types.","commonSituations":"Optimization pipelines that recompress texture atlases to webp; texture folders named with uppercase .PNG (case-sensitive endsWith); partial downloads of large model archives.","solutions":["Re-export or re-package the model keeping the original .png texture atlases (the fake-settings path only recognizes .png)","Include the real .model3.json so FileReferences.Textures from the editor export is used rather than the png-only heuristic","Pre-scan uploads: if no file ends with '.png', reject with guidance before calling the loader","Check for nested zips and extract them first so texture paths are visible to the loader"],"exampleFix":"// before\nconst settings = FileLoader.createSettings(files) // 'Textures not found'\n\n// after\nconst hasPng = files.some(f => f.name.toLowerCase().endsWith('.png'))\nif (!hasPng) {\n  throw new Error('No .png textures found. Keep original PNG texture files, or include the model\\'s .model3.json so texture paths are read from it.')\n}","handlingStrategy":"validation","validationCode":"const hasPng = files.some(f => f.name.toLowerCase().endsWith('.png'))\nif (!hasPng) throw new Error('Model needs its original .png texture atlases')","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.message === 'Textures not found') { showUserError('No PNG textures in this archive; re-export with original textures'); return } throw e }","preventionTips":["Don't convert Live2D texture atlases to webp/jpg","Include the settings file so texture paths come from FileReferences","Check nested zips before loading"],"tags":["live2d","zip","textures","missing-assets"],"backgroundTag":"live2d-model-load-failed","analyzedSha":"9c213115f8bd0fff9e6eabab02b077ac32da21be","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}