tinyhumansai/openhuman · error
mascot manifest: no renderable mascots
Error message
mascot manifest: no renderable mascots
What it means
Thrown by parseManifest() when the fetched document has a mascots array but every entry fails isManifestEntry() filtering. Per the doc comment the function silently drops individual malformed entries (one bad entry should never blank the library), so reaching zero renderable entries means the payload is structurally present but entirely non-playable — e.g. every entry is missing its runtime .riv file reference.
Source
Thrown at app/src/features/human/Mascot/manifest/manifestService.ts:116
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 {
try {
const raw = window.localStorage.getItem(SNAPSHOT_KEY);
if (!raw) return null;
return parseManifest(JSON.parse(raw));View on GitHub (pinned to 7491200858)
Solutions
- Inspect the fetched manifest and run the same isManifestEntry() predicate per entry locally to see which check fails (fields vs runtime file).
- If assets moved, fix the manifest generator or the entry paths so runtimeFile() resolves for at least one mascot.
- If upstream is genuinely empty, pin MASCOT_MANIFEST_URL to the last known-good manifest revision (e.g. a tagged commit path).
- Catch and fall back to the persisted snapshot or a bundled manifest so the mascot UI degrades gracefully.
Example fix
// before
const manifest = parseManifest(await res.json());
// after
let manifest: MascotManifest;
try {
manifest = parseManifest(await res.json());
} catch {
const snapshot = readSnapshot();
if (!snapshot) throw err;
manifest = snapshot;
} Defensive patterns
Strategy: fallback
Validate before calling
const doc = raw as { mascots?: unknown[] };
const renderable = (doc.mascots ?? []).filter(isManifestEntry);
if (renderable.length === 0) { /* fall back to snapshot */ } Try / catch
try {
return parseManifest(raw);
} catch (err) {
const snapshot = readSnapshot();
if (snapshot) return snapshot;
throw err;
} Prevention
- Generate the manifest only after assets are committed and verified present.
- Run isManifestEntry over generated entries in CI to catch all-filtered payloads before publish.
- Version the manifest schema and regenerate atomically with the asset deploy.
When it happens
Trigger: A manifest whose mascots array is populated (length > 0) but for which isManifestEntry() returns false for all items — missing required id/name fields, or runtimeFile(entry) resolves to nothing so no asset is playable. Common when a generation script runs before any .riv assets were committed, or when the .rev/runtime file check fails for every entry due to a renamed asset directory.
Common situations: Upstream mascot repo emptied or renamed its assets directory while the manifest still lists entries; manifest generated against a different schema version where entries pass the raw-shape check but fail the runtime-file check; partial deploy where JSON updated but binaries did not.
Related errors
- mascot manifest: missing mascots array
- manifest fetch failed (${res.status})
- failed to fetch riv (${res.status}) from ${url}
- audio blob is empty
- audio blob is empty
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/2d615fb3db9181a7.
Report an issue: GitHub.