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
- Upload an archive/folder that contains the .model3.json (Cubism 4) or .model.json (Cubism 2) next to its assets
- If you must accept bare moc+textures, detect that case first and build fake settings via createFakeSettings instead of FileLoader.createSettings
- Filter client-side: warn when no selected file matches /\.model3?\.json$/i before invoking the loader
- 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
- Use directory upload (webkitdirectory) so webkitRelativePath is populated
- Filter/validate the selection client-side before invoking the loader
- Educate users to drop the whole model folder, not the textures subfolder
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
- A motion sample occurs after the recording duration.
- Archive entry not found
- Cannot find file
- Cannot set motion: model not loaded
- Empty settings file
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)