moeru-ai/airi · error · Error

Archive entry not found: ${filePath}

Error message

Archive entry not found: ${filePath}

What it means

readJsonObject looks up an entry inside a parsed JSZip archive by exact path and parses it as a JSON object. It throws this Error when zip.file(filePath) returns undefined, meaning no entry with that exact path exists in the ZIP. validateLive2DZip calls it for the model's settings/display JSON files discovered earlier in the archive listing.

Source

Thrown at packages/stage-ui-live2d/src/utils/live2d-validator.ts:183

    if (!Array.isArray(definitions))
      continue

    for (const definition of definitions) {
      if (!isRecord(definition))
        continue

      const file = readString(definition.File)
      if (file)
        references.push(file)
    }
  }
  return references
}

async function readJsonObject(zip: JSZip, filePath: string): Promise<Record<string, unknown>> {
  const file = zip.file(filePath)
  if (!file)
    throw new Error(`Archive entry not found: ${filePath}`)

  const value: unknown = JSON.parse(await file.async('text'))
  if (!isRecord(value))
    throw new TypeError(`Expected a JSON object in ${filePath}`)
  return value
}

function addIssue(
  report: Live2DValidationReport,
  code: Live2DValidationIssueCode,
  severity: Live2DValidationIssueSeverity,
  message: string,
  resolution: string,
): void {
  report.issues.push({ code, severity, message, resolution })
}

function checkReference(

View on GitHub (pinned to 9c213115f8)

Solutions

  1. Open the ZIP and verify the exact entry name; align the requested filePath with the entry (case, extension, separators)
  2. Normalize paths (strip leading './', '/', decode percent-encoding, convert backslashes to '/') before zip.file lookup
  3. Re-export the ZIP so entry names match what the settings JSON references
  4. Check that the referenced file was not stripped when the archive was built (some tools exclude unused files)

Example fix

// before
const file = zip.file('./model/model3.json') // entry is 'model/model3.json'
// after
const normalized = filePath.replace(/^\.?\//, '').replace(/\\/g, '/')
const file = zip.file(normalized)
Defensive patterns

Strategy: try-catch

Validate before calling

function hasArchiveEntry(zip: JSZip, filePath: string): boolean {
  const normalized = filePath.replace(/^\.?\//, '').replace(/\\/g, '/')
  return !!zip.file(normalized)
}
// check every referenced JSON path with hasArchiveEntry before validating

Type guard

function zipHasFile(zip: JSZip, path: string): boolean {
  return typeof zip.file(path)?.async === 'function'
}

Try / catch

try {
  const info = await readJsonObject(zip, filePath)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Archive entry not found:')) {
    const missing = e.message.slice('Archive entry not found: '.length)
    // list zip.files, suggest nearest match (case/separator diff)
  } else throw e
}

Prevention

When it happens

Trigger: Calling validateLive2DZip (or countParsedJsonResources/displayInfo) with a ZIP whose referenced JSON path does not match any archive entry exactly — e.g. path case mismatch, leading './' or '/' prefixes, URL-encoded characters, or a settings file listing a nested model3.json path that is absent.

Common situations: Live2D settings files referencing textures/motions with paths differing in case or separators from actual entries; ZIPs created on different OSes normalizing names differently; renamed files inside the archive without updating the settings JSON.

Related errors


AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02). Data as JSON: /api/errors/bce5595dd726aada. Report an issue: GitHub.