moeru-ai/airi · warning
[ZipLoader] Failed to extract CDI/EXP metadata:
Error message
[ZipLoader] Failed to extract CDI/EXP metadata:
What it means
The custom ZipLoader.createSettings in live2d-zip-loader scans the model archive for .cdi3.json and .exp3.json entries, reads them, and JSON.parse's their contents into settings._cdiData / settings._expFiles. The whole extraction is wrapped in try/catch: any failure (missing-but-referenced entry, unreadable file, invalid JSON, a non-null assertion hitting a null file) is warned here and the settings are returned without the metadata — the model still loads, minus expressions/CDI data.
Source
Thrown at packages/stage-ui-live2d/src/utils/live2d-zip-loader.ts:74
// Find and collect expression files
const expPaths = filePaths.filter(f => f.toLowerCase().endsWith('.exp3.json'))
if (expPaths.length > 0) {
const expFiles: Array<{ name: string, fileName: string, data: unknown }> = []
for (const expPath of expPaths) {
const expText = await reader.file(expPath)!.async('text')
const baseName = expPath.split('/').pop()?.replace('.exp3.json', '') || expPath
expFiles.push({
name: baseName,
fileName: expPath,
data: JSON.parse(expText),
})
}
metadataSettings._expFiles = expFiles
console.info('[ZipLoader] Extracted', expFiles.length, 'expression files')
}
}
catch (e) {
console.warn('[ZipLoader] Failed to extract CDI/EXP metadata:', e)
}
return settings
}
/**
* Normalizes Live2D model settings JSON before upstream path resolution.
*
* Before:
* - `{ "FileReferences": { "Physics": null } }`
*
* After:
* - `{ "FileReferences": {} }`
*/
function sanitizeModelSettingsText(text: string): string {
const json = JSON.parse(text) as Record<string, unknown>
const refs = json.FileReferences
View on GitHub (pinned to 9c213115f8)
Solutions
- Open the model zip and validate every .cdi3.json / .exp3.json parses as JSON (e.g. with a quick node script or unzip + jq)
- This is non-fatal: if expressions are optional for your use case, proceed — the model renders without them
- Re-export the model from Live2D Cubism with expression files properly included, or fix the broken entries and re-zip preserving UTF-8 names
Example fix
// before (inside createSettings)
const expText = await reader.file(expPath)!.async('text')
expFiles.push({ name: baseName, fileName: expPath, data: JSON.parse(expText) })
// after (per-file tolerance: one bad entry doesn't drop all metadata)
const entry = reader.file(expPath)
if (!entry)
continue
try {
const parsed = JSON.parse(await entry.async('text'))
expFiles.push({ name: baseName, fileName: expPath, data: parsed })
}
catch (e) {
console.warn('[ZipLoader] Skipping bad expression file:', expPath, e)
} Defensive patterns
Strategy: fallback
Validate before calling
// validate before relying on expression metadata
for (const path of Object.keys(zip.files).filter(f => f.toLowerCase().endsWith('.exp3.json'))) {
const entry = zip.file(path)
if (!entry)
continue
JSON.parse(await entry.async('text')) // throws early on corrupted entries
} Type guard
function isExpMetadata(value: unknown): value is Array<{ name: string, fileName: string, data: unknown }> {
return Array.isArray(value) && value.every(item => typeof item?.name === 'string' && typeof item?.fileName === 'string')
} Prevention
- Validate model archives at import time (the repo's validateLive2DZip) so corrupted exp/cdi entries are rejected with a clear message
- Treat expression metadata as optional: design features to degrade without it
- Preserve UTF-8 entry names when re-zipping models
When it happens
Trigger: An archive whose cdi3/exp3 entries contain malformed JSON, are zero-byte, have paths with legacy codepage encoding that don't match, or where reader.file(path) returns null for a path found by suffix but not actually present as a file.
Common situations: Hand-repacked or VTube-Studio-exported zips with corrupted expression JSON; case-mismatched file names; mojibake entry names on non-ASCII expression files.
Related errors
- [Model.vue] Expression controller initialization failed:
- [Model.vue] Expression controller initialisation failed:
- Empty settings file: ${url}
- Expected exactly one moc file, got ${mocFiles.length} ${file
- Textures not found
AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-08-18).
Data as JSON: /api/errors/8dc7a589c21828fb.
Report an issue: GitHub.