{"record":{"id":"31aa5f442108f52b","repo":"moeru-ai/airi","slug":"settings-file-not-found","errorCode":null,"errorMessage":"Settings file not found","messagePattern":"Settings file not found","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts","lineNumber":223,"sourceCode":"\nZipLoader.readText = async (jsZip: JSZip, path: string) => {\n  const file = jsZip.file(path)\n\n  if (!file) {\n    throw new Error(`Cannot find file: ${path}`)\n  }\n\n  const text = await file.async('text')\n\n  return isSettingsFile(path) ? sanitizeModelSettingsText(text) : text\n}\n\nconst defaultFileLoaderReadText = FileLoader.readText\nFileLoader.createSettings = async (files: File[]) => {\n  const settingsFile = files.find(file => isSettingsFile(file.webkitRelativePath || file.name))\n\n  if (!settingsFile) {\n    throw new TypeError('Settings file not found')\n  }\n\n  const settingsUrl = settingsFile.webkitRelativePath || settingsFile.name\n  const settingsText = await FileLoader.readText(settingsFile)\n  const settings = createModelSettings(settingsText, settingsUrl)\n  Object.assign(settings, { _objectURL: URL.createObjectURL(settingsFile) })\n\n  return settings\n}\n\nFileLoader.readText = async (file: File) => {\n  const text = await defaultFileLoaderReadText(file)\n  const path = file.webkitRelativePath || file.name\n\n  return isSettingsFile(path) ? sanitizeModelSettingsText(text) : text\n}\n\nZipLoader.getFilePaths = (jsZip: JSZip) => {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts#L205-L241","documentation":"`FileLoader.createSettings(files)` searches the uploaded file list for a settings file (`isSettingsFile` — a `.model3.json`/`.model.json`, excluding ignored entries). If none is found it throws `Settings file not found` (as a `TypeError`). This is the entry-point used when the user selects files/folders through an `<input>` rather than a ZIP.","triggerScenarios":"The user selected a folder or set of files that contains no `.model3.json` / `.model.json`; the settings file has an unexpected extension; the settings file is present but matched by `shouldIgnoreLive2DArchiveEntry` or is `items_pinned_to_model.json`.","commonSituations":"User selected only the moc/textures but not the manifest; user selected a non-Live2D folder; the `.model3.json` was renamed; webkitRelativePath not exposed so the name check failed; the folder contains a settings file that the ignore filter excluded.","solutions":["Instruct the user to select the folder that contains the `.model3.json` (the whole model folder, not just textures).","Pre-validate the file list before calling `FileLoader.createSettings`: `files.some(f => isSettingsFile(f.webkitRelativePath || f.name))`.","Confirm the settings file is not being caught by `shouldIgnoreLive2DArchiveEntry`.","Fall back to ZIP upload if folder selection omits the manifest."],"exampleFix":"// before\nconst settings = await FileLoader.createSettings(selectedFiles)  // throws\n\n// after\nconst hasSettings = selectedFiles.some(f => isSettingsFile(f.webkitRelativePath || f.name))\nif (!hasSettings)\n  throw new Error('Select the model folder containing the .model3.json file')\nconst settings = await FileLoader.createSettings(selectedFiles)","handlingStrategy":"validation","validationCode":"const ok = files.some(f => isSettingsFile(f.webkitRelativePath || f.name))\nif (!ok) throw new Error('Select the model folder containing the .model3.json')\nawait FileLoader.createSettings(files)","typeGuard":"function hasLive2DSettingsFile(files: File[]): boolean {\n  return files.some(f => isSettingsFile(f.webkitRelativePath || f.name))\n}","tryCatchPattern":"try {\n  await FileLoader.createSettings(files)\n} catch (e) {\n  if ((e instanceof Error || e instanceof TypeError) && /Settings file not found/.test(e.message)) {\n    // prompt user to select the whole model folder\n  } else throw e\n}","preventionTips":["Have users select the full model folder, not individual textures.","Pre-validate the file list for a settings file before loading.","Offer ZIP upload as a fallback when folder selection omits the manifest."],"tags":["live2d","model-loading","file-input","input-validation"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}