{"record":{"id":"bc1b466e5b4e1980","repo":"moeru-ai/airi","slug":"expected-a-json-object-in-filepath","errorCode":null,"errorMessage":"Expected a JSON object in ${filePath}","messagePattern":"Expected a JSON object in (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/stage-ui-live2d/src/utils/live2d-validator.ts","lineNumber":187,"sourceCode":"      if (!isRecord(definition))\n        continue\n\n      const file = readString(definition.File)\n      if (file)\n        references.push(file)\n    }\n  }\n  return references\n}\n\nasync function readJsonObject(zip: JSZip, filePath: string): Promise<Record<string, unknown>> {\n  const file = zip.file(filePath)\n  if (!file)\n    throw new Error(`Archive entry not found: ${filePath}`)\n\n  const value: unknown = JSON.parse(await file.async('text'))\n  if (!isRecord(value))\n    throw new TypeError(`Expected a JSON object in ${filePath}`)\n  return value\n}\n\nfunction addIssue(\n  report: Live2DValidationReport,\n  code: Live2DValidationIssueCode,\n  severity: Live2DValidationIssueSeverity,\n  message: string,\n  resolution: string,\n): void {\n  report.issues.push({ code, severity, message, resolution })\n}\n\nfunction checkReference(\n  report: Live2DValidationReport,\n  archivePaths: string[],\n  options: ReferenceCheckOptions,\n): boolean {","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/moeru-ai/airi/blob/9c213115f8bd0fff9e6eabab02b077ac32da21be/packages/stage-ui-live2d/src/utils/live2d-validator.ts#L169-L205","documentation":"After successfully reading an archive entry, readJsonObject parses its text with JSON.parse and requires the result to be a plain object (isRecord). It throws a TypeError when the entry's content is not a JSON object — malformed JSON, or valid JSON whose top level is an array, string, number, or null. Live2D metadata files are expected to be objects.","triggerScenarios":"Calling validateLive2DZip (or displayInfo/countParsedJsonResources) on a ZIP where a JSON entry is truncated/corrupted during compression, is a JSON array instead of an object, or is not JSON at all despite its extension (e.g. an HTML error page saved as .json).","commonSituations":"Build tools minifying/transforming JSON into non-object output; text-encoding corruption (BOM or binary content) breaking JSON.parse; exporting settings that serialize as arrays; hand-edited JSON with trailing commas.","solutions":["Open the entry's text and validate it with JSON.parse locally; fix syntax errors (trailing commas, truncation)","Ensure the top level is an object: wrap bare arrays or scalars in an object in the source file","Re-export/re-save the file with UTF-8 encoding without BOM, confirming it is valid JSON","Replace files whose extension is .json but whose content is HTML or binary"],"exampleFix":"// before (file content)\n[{ \"id\": \"motion\" }]\n// after\n{ \"motions\": [{ \"id\": \"motion\" }] }","handlingStrategy":"type-guard","validationCode":"function isJsonObjectText(text: string): boolean {\n  try {\n    const v: unknown = JSON.parse(text)\n    return typeof v === 'object' && v !== null && !Array.isArray(v)\n  } catch { return false }\n}\n// verify each .json entry's text with isJsonObjectText before validating","typeGuard":"function isRecord(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n}","tryCatchPattern":"try {\n  const obj = await readJsonObject(zip, filePath)\n} catch (e) {\n  if (e instanceof TypeError && e.message.startsWith('Expected a JSON object in')) {\n    // report the file path; inspect its raw text to find the malformed content\n  } else throw e\n}","preventionTips":["Validate every .json file in the model ZIP with JSON.parse before shipping it","Save JSON as UTF-8 without BOM; avoid binary content under a .json extension","Ensure top-level structure is an object, not an array or scalar","Avoid hand edits that leave trailing commas or truncated files; use a linter/formatter for JSON"],"tags":["live2d","zip","json"],"backgroundTag":"invalid-json-object","analyzedSha":"9c213115f8bd0fff9e6eabab02b077ac32da21be","analyzedAt":"2026-09-02T04:27:24.639Z","contentChangedAt":"2026-09-02T04:27:24.639Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}