{"record":{"id":"2e9758c851ef2f53","repo":"moeru-ai/airi","slug":"empty-settings-file-url","errorCode":null,"errorMessage":"Empty settings file: ${url}","messagePattern":"Empty settings file: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts","lineNumber":131,"sourceCode":"function normalizeLive2DArchivePath(path: string): string {\n  try {\n    return decodeURI(path)\n  }\n  catch {\n    // Malformed percent escapes cannot be URI-decoded and therefore represent a literal archive path.\n    return path\n  }\n}\n\nfunction useArchivePathResolution(settings: ModelSettings): ModelSettings {\n  const resolveURL = settings.resolveURL.bind(settings)\n  settings.resolveURL = path => normalizeLive2DArchivePath(resolveURL(path))\n  return settings\n}\n\nfunction createModelSettings(text: string, url: string): ModelSettings {\n  if (!text) {\n    throw new Error(`Empty settings file: ${url}`)\n  }\n\n  const settingsJSON = JSON.parse(text) as JSONObject & { url?: string }\n  settingsJSON.url = url\n  const runtime = Live2DFactory.findRuntime(settingsJSON)\n\n  if (!runtime) {\n    throw new Error('Unknown settings JSON')\n  }\n\n  return useArchivePathResolution(runtime.createModelSettings(settingsJSON))\n}\n\nexport function isSettingsFile(file: string) {\n  return !shouldIgnoreLive2DArchiveEntry(file)\n    && !file.endsWith('items_pinned_to_model.json')\n    && (file.endsWith('.model3.json') || file.endsWith('.model.json'))\n}","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/moeru-ai/airi/blob/9c213115f8bd0fff9e6eabab02b077ac32da21be/packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts#L113-L149","documentation":"Thrown by createModelSettings in the Live2D ZIP loader when the text read for a settings file (.model3.json / .model.json) is an empty string. The loader found a settings entry by name but its content is blank, so JSON.parse would fail later anyway; this check fails fast with the offending `url` in the message.","triggerScenarios":"Loading a ZIP whose .model3.json entry is 0 bytes; a settings entry that contains only whitespace after sanitizeModelSettingsText; selecting an empty/truncated file in the FileLoader.createSettings upload path which reads text then calls createModelSettings.","commonSituations":"Broken export from Live2D Cubism editor (empty model3.json), ZIP truncated during download or by git-lfs pointer mishandling, archives packaged by tools that write placeholder files, or an interrupted save leaving a blank file.","solutions":["Open the archive and verify the .model3.json actually has content (e.g. unzip -l for size, then cat the file)","Re-export the model from the Cubism editor or re-download the archive if the entry is 0 bytes","Wrap loads in try/catch and show the `url` from the message so users know which file in their ZIP is blank","If you generate archives programmatically, assert entry text is non-empty before adding it"],"exampleFix":"// before\nconst settings = createModelSettings(text, url) // text === ''\n\n// after\nif (!text.trim()) {\n  throw new Error(`Settings file \"${url}\" is empty — the archive is truncated or the export failed. Re-export and re-zip the model.`)\n}\nconst settings = createModelSettings(text, url)","handlingStrategy":"validation","validationCode":"const text = await zipEntry.async('text')\nif (!text.trim()) {\n  throw new Error(`Settings entry \"${path}\" is empty — re-export or re-download the archive`)\n}\nconst settings = createModelSettings(text, path)","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.message.startsWith('Empty settings file')) { showUserError(`The model's settings file (${e.message.split(': ')[1]}) is blank; the ZIP is corrupt`); return } throw e }","preventionTips":["Check entry sizes when unzipping (0-byte model3.json is the signature)","Re-export from the Cubism editor rather than hand-editing archives","Report the offending url from the message so users can self-serve"],"tags":["live2d","zip","empty-file","model-loading"],"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"}