moeru-ai/airi · error · TypeError

Settings file not found

Error message

Settings file not found

What it means

Thrown as a TypeError by the FileLoader.createSettings override (live2d-zip-loader) when none of the uploaded File objects matches isSettingsFile — i.e. no file whose webkitRelativePath or name ends in .model3.json or .model.json (excluding ignored entries and items_pinned_to_model.json). The upload path requires a settings file to bootstrap model creation; fake settings from a bare moc are not attempted here.

Solutions

  1. Upload an archive/folder that contains the .model3.json (Cubism 4) or .model.json (Cubism 2) next to its assets
  2. If you must accept bare moc+textures, detect that case first and build fake settings via createFakeSettings instead of FileLoader.createSettings
  3. Filter client-side: warn when no selected file matches /\.model3?\.json$/i before invoking the loader
  4. Ensure directory upload is used (webkitdirectory) so webkitRelativePath is populated and nested settings are found

Example fix

// before
const settings = await FileLoader.createSettings(selectedFiles) // TypeError('Settings file not found')

// after
const settingsFile = selectedFiles.find(f => /\.model3?\.json$/i.test(f.webkitRelativePath || f.name))
if (!settingsFile) {
  throw new Error('Select the model folder including its .model3.json (or .model.json) settings file.')
}
const settings = await FileLoader.createSettings(selectedFiles)
Defensive patterns

Strategy: validation

Validate before calling

const hasSettings = files.some(f => /\.model3?\.json$/i.test(f.webkitRelativePath || f.name))
if (!hasSettings) throw new Error('Select the model folder containing its .model3.json / .model.json')
const settings = await FileLoader.createSettings(files)

Type guard

const isSettingsUpload = (files: File[]): boolean => files.some(f => /\.model3?\.json$/i.test(f.webkitRelativePath || f.name))

Try / catch

catch (e) { if (e instanceof TypeError && e.message === 'Settings file not found') { showUploadHint(); return } throw e }

Prevention

When it happens

Trigger: Calling FileLoader.createSettings with a folder upload lacking any .model3.json/.model.json; uploading only the moc and textures; uploading a .zip File directly (a zip is not unpacked by this API); settings file named .model3.JSON (uppercase) since endsWith is case-sensitive.

Common situations: Users dropping the texture folder instead of the model root; directory uploads where webkitRelativePath is empty because files were picked file-by-file; case-sensitivity on Windows-created archives; distributing models without their settings export.

Related errors


AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-08-18). Data as JSON: /api/errors/31aa5f442108f52b. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts:224

ZipLoader.readText = async (jsZip: JSZip, path: string) => {
  const file = jsZip.file(path)

  if (!file) {
    throw new Error(`Cannot find file: ${path}`)
  }

  const text = await file.async('text')

  return isSettingsFile(path) ? sanitizeModelSettingsText(text) : text
}

const defaultFileLoaderReadText = FileLoader.readText
FileLoader.createSettings = async (files: File[]) => {
  const settingsFile = files.find(file => isSettingsFile(file.webkitRelativePath || file.name))

  if (!settingsFile) {
    throw new TypeError('Settings file not found')
  }

  const settingsUrl = settingsFile.webkitRelativePath || settingsFile.name
  const settingsText = await FileLoader.readText(settingsFile)
  const settings = createModelSettings(settingsText, settingsUrl)
  Object.assign(settings, { _objectURL: URL.createObjectURL(settingsFile) })

  return settings
}

FileLoader.readText = async (file: File) => {
  const text = await defaultFileLoaderReadText(file)
  const path = file.webkitRelativePath || file.name

  return isSettingsFile(path) ? sanitizeModelSettingsText(text) : text
}

ZipLoader.getFilePaths = (jsZip: JSZip) => {

View on GitHub (pinned to 9c213115f8)