tinyhumansai/openhuman · warning
manifest fetch failed (${res.status})
Error message
manifest fetch failed (${res.status}) What it means
fetchMascotManifest() fetches the mascot manifest over HTTP with an AbortController-backed timeout (MANIFEST_FETCH_TIMEOUT_MS). This error is thrown when the response arrives but res.ok is false — i.e. a non-2xx HTTP status such as 404 or 503. Importantly the surrounding catch then tries readSnapshot(); this Error only reaches the caller when no snapshot fallback exists.
Source
Thrown at app/src/features/human/Mascot/manifest/manifestService.ts:166
/**
* Fetch the mascot manifest, memoised for the session. On a network failure we
* fall back to the last localStorage snapshot so the picker still works
* offline; if there is no snapshot either, the rejection propagates so the UI
* can show an error state.
*/
export function fetchMascotManifest(): Promise<MascotManifest> {
if (inflight) return inflight;
inflight = (async () => {
const controller = new AbortController();
const timeoutId = window.setTimeout(() => controller.abort(), MANIFEST_FETCH_TIMEOUT_MS);
try {
log('fetching manifest %s', MASCOT_MANIFEST_URL);
const res = await fetch(MASCOT_MANIFEST_URL, {
cache: 'no-cache',
signal: controller.signal,
});
if (!res.ok) throw new Error(`manifest fetch failed (${res.status})`);
const manifest = parseManifest(await res.json());
log('manifest ok — %d mascots (schema v%d)', manifest.mascots.length, manifest.schemaVersion);
writeSnapshot(manifest);
return manifest;
} catch (err) {
const snapshot = readSnapshot();
if (snapshot) {
log('manifest fetch failed, using snapshot: %o', err);
return snapshot;
}
// Reset so a later retry can attempt the network again rather than
// re-resolving this rejected promise forever.
inflight = null;
throw err;
} finally {
window.clearTimeout(timeoutId);
}
})();View on GitHub (pinned to 7491200858)
Solutions
- curl -I the manifest URL and check the status; 404 means update MASCOT_MANIFEST_URL, 403 means rate limiting (wait or switch to a pinned commit URL), 5xx means upstream outage — retry later.
- If behind a proxy, allowlist the manifest host or serve the manifest locally in dev.
- Ship/seed a snapshot or bundled manifest so first-run users degrade gracefully instead of seeing the error.
- Reduce no-cache pressure in dev by pointing MASCOT_MANIFEST_URL at a local static server.
Example fix
// before
const res = await fetch(MASCOT_MANIFEST_URL, { cache: 'no-cache', signal: controller.signal });
if (!res.ok) throw new Error(`manifest fetch failed (${res.status})`);
// after — retry once on 5xx before falling back
let res = await fetch(MASCOT_MANIFEST_URL, { cache: 'no-cache', signal: controller.signal });
if (res.status >= 500) {
await delay(1500);
res = await fetch(MASCOT_MANIFEST_URL, { cache: 'no-cache', signal: controller.signal });
}
if (!res.ok) throw new Error(`manifest fetch failed (${res.status})`); Defensive patterns
Strategy: fallback
Try / catch
try {
const manifest = await fetchMascotManifest();
} catch (err) {
// fetchMascotManifest already falls back to snapshot when present;
// ensure one is seeded on first run or bundle a default manifest.
} Prevention
- Seed/bundle a default manifest so first-run never depends on the network.
- Retry transient 5xx once with backoff before surfacing failure.
- Monitor the manifest URL (status + shape) with a synthetic check.
When it happens
Trigger: GET MASCOT_MANIFEST_URL returns 404 (manifest file removed/renamed upstream), 403 (rate-limited by raw CDN), 5xx (CDN or origin failure), or a captive-portal proxy returning an error page. Also triggered when the AbortController fires and fetch rejects with an AbortError — that surfaces as a different message, so this specific message is strictly an HTTP status failure.
Common situations: The manifest lives on a CDN/repo URL that was restructured; GitHub raw rate limits after many dev reloads (cache:'no-cache' defeats browser caching, amplifying request volume); corporate proxy blocking the host; first run with no snapshot cached so any network hiccup becomes user-visible.
Related errors
- mascot manifest: missing mascots array
- mascot manifest: no renderable mascots
- failed to fetch riv (${res.status}) from ${url}
- Smithery returned HTTP {status}: {}
- Failed to send magic link (${response.status})
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/e0b186df5b49bf1d.
Report an issue: GitHub.