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

  1. Inspect the fetched manifest and run the same isManifestEntry() predicate per entry locally to see which check fails (fields vs runtime file).
  2. If assets moved, fix the manifest generator or the entry paths so runtimeFile() resolves for at least one mascot.
  3. If upstream is genuinely empty, pin MASCOT_MANIFEST_URL to the last known-good manifest revision (e.g. a tagged commit path).
  4. 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

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


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/2d615fb3db9181a7. Report an issue: GitHub.