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

  1. 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.
  2. If behind a proxy, allowlist the manifest host or serve the manifest locally in dev.
  3. Ship/seed a snapshot or bundled manifest so first-run users degrade gracefully instead of seeing the error.
  4. 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

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


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