tinyhumansai/openhuman · error
mascot manifest: missing mascots array
Error message
mascot manifest: missing mascots array
What it means
parseManifest() validates the fetched mascot library manifest document. It throws 'mascot manifest: missing mascots array' when the parsed JSON is null/undefined or lacks a mascots property that is an Array. This is a hard failure on a fundamentally malformed payload — the fetch succeeded (HTTP 200) but the body does not resemble a manifest at all.
Source
Thrown at app/src/features/human/Mascot/manifest/manifestService.ts:112
(!Array.isArray(se.channels) || !se.channels.every(isManifestChannel))
) {
return false;
}
if (!Array.isArray(m.files) || !m.files.every(isManifestFile)) return false;
// A renderable mascot needs a runtime `.riv`; drop entries that only ship a
// source `.rev` so callers never select an unplayable asset.
return !!runtimeFile(m as MascotManifestEntry);
}
/**
* Validate and normalise a parsed manifest document. Throws on a
* fundamentally malformed payload; silently drops individual malformed
* mascot entries (a single bad entry should never blank the whole library).
*/
export function parseManifest(raw: unknown): MascotManifest {
const doc = raw as Partial<MascotManifest> | null;
if (!doc || !Array.isArray(doc.mascots)) {
throw new Error('mascot manifest: missing mascots array');
}
const mascots = doc.mascots.filter(isManifestEntry);
if (mascots.length === 0) {
throw new Error('mascot manifest: no renderable mascots');
}
return {
schemaVersion: typeof doc.schemaVersion === 'number' ? doc.schemaVersion : 1,
generatedAt: isNonEmptyString(doc.generatedAt) ? doc.generatedAt : '',
mascots,
source: {
repository: doc.source?.repository ?? '',
branch: doc.source?.branch ?? '',
commit: doc.source?.commit ?? '',
},
};
}
function readSnapshot(): MascotManifest | null {View on GitHub (pinned to 7491200858)
Solutions
- Open MASCOT_MANIFEST_URL in a browser/curl and inspect the body — confirm it is JSON with a top-level "mascots": [...] array.
- If the body is HTML (repo renamed, Pages fallback), update MASCOT_MANIFEST_URL to the correct manifest location.
- If the manifest is genuinely broken upstream, fix the generator to always emit the mascots array; note fetchMascotManifest() falls back to an IndexedDB/localStorage snapshot when available, so clearing the snapshot store makes the failure reproducible.
- As a consumer, catch the error and fall back to a bundled default manifest instead of blanking the mascot feature.
Example fix
// before
const manifest = parseManifest(await res.json());
// after — hard-fail only on truly wrong shape, else fall back
let manifest: MascotManifest;
try {
manifest = parseManifest(await res.json());
} catch (err) {
manifest = bundledFallbackManifest; // shipped default
warnLog('mascot manifest unusable, using bundled fallback', err);
} Defensive patterns
Strategy: validation
Validate before calling
const raw: unknown = await res.json();
const looksLikeManifest =
!!raw && typeof raw === 'object' && Array.isArray((raw as any).mascots);
if (!looksLikeManifest) { /* use fallback manifest */ } Type guard
function isManifestDoc(v: unknown): v is { mascots: unknown[] } {
return !!v && typeof v === 'object' && Array.isArray((v as { mascots?: unknown }).mascots);
} Try / catch
try {
manifest = parseManifest(raw);
} catch (err) {
if (err instanceof Error && err.message.startsWith('mascot manifest:')) {
manifest = bundledFallback;
} else throw err;
} Prevention
- Pin the manifest URL to an immutable revision (tagged commit) rather than a moving branch.
- Keep a snapshot/bundled fallback so malformed upstream payloads never blank the feature.
- Validate the manifest in CI when it is generated, not only at read time.
When it happens
Trigger: fetchMascotManifest() receives a 200 response whose JSON body is null, an empty object, or has mascots as a non-array (string, object, number). Typical causes: CDN/GitHub Pages returning an HTML error or SPA fallback page that happens to parse as JSON-null, a truncated JSON body, or a manifest generation script emitting {schemaVersion, generatedAt} without the mascots key.
Common situations: The manifest URL (MASCOT_MANIFEST_URL) points at a raw.githubusercontent/Pages URL that 404s into an HTML fallback; a CI job regenerated the manifest with a bug dropping the array; an upstream repo restructured and renamed the manifest file; response caching serving a corrupted body.
Related errors
- mascot manifest: no renderable mascots
- manifest fetch failed (${res.status})
- Failed to parse service CLI output as JSON: parsed value doe
- failed to fetch riv (${res.status}) from ${url}
- audio blob is empty
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/8c1a9847a8f6cb8c.
Report an issue: GitHub.