tinyhumansai/openhuman · warning
failed to fetch riv (${res.status}) from ${url}
Error message
failed to fetch riv (${res.status}) from ${url} What it means
rivCache.ts caches .riv (Rive) binary assets in IndexedDB. fetchBuffer() performs the plain network fetch for a mascot's animation file and throws this error when the response status is not ok, embedding both the HTTP status and the full URL for diagnosis. Callers hit this inside loadRivBuffer() when the cached version is absent or differs from the requested version, forcing a network fetch.
Source
Thrown at app/src/features/human/Mascot/rivCache.ts:93
}
function writeEntry(db: IDBDatabase, entry: RivCacheEntry): Promise<void> {
return new Promise(resolve => {
try {
const tx = db.transaction(STORE, 'readwrite');
tx.objectStore(STORE).put(entry);
tx.oncomplete = () => resolve();
tx.onerror = () => resolve();
tx.onabort = () => resolve();
} catch {
resolve();
}
});
}
async function fetchBuffer(url: string): Promise<ArrayBuffer> {
const res = await fetch(url);
if (!res.ok) throw new Error(`failed to fetch riv (${res.status}) from ${url}`);
return res.arrayBuffer();
}
/**
* Resolve the .riv binary for a mascot, hitting the network only when the
* cached version differs from `version`. Returns an ArrayBuffer suitable for
* `useRive({ buffer })`.
*/
export async function loadRivBuffer(
id: string,
version: string,
url: string
): Promise<ArrayBuffer> {
const mem = memCache.get(id);
if (mem && mem.version === version) {
cacheLog('mem hit %s@%s', id, version);
return mem.buffer;
}View on GitHub (pinned to 7491200858)
Solutions
- Copy the exact URL from the error message and curl it — 404 means fix the manifest entry or the asset location; 403/429 means rate limiting, back off or pin to a release CDN.
- If the manifest is stale, clear the cached manifest snapshot so fetchMascotManifest() refetches a consistent manifest+URLs pair.
- Catch per-mascot render errors and fall back to a bundled/default mascot so one broken asset does not break the feature.
- Verify the version string passed to loadRivBuffer matches the manifest's version to avoid needless cache-miss refetches.
Example fix
// before
try {
buffer = await loadRivBuffer(id, version, url);
} catch (err) {
throw err;
}
// after
try {
buffer = await loadRivBuffer(id, version, url);
} catch (err) {
warnLog('mascot asset unavailable, using default', { id, err });
buffer = await loadRivBuffer(DEFAULT_MASCOT_ID, DEFAULT_MASCOT_VERSION, DEFAULT_MASCOT_URL);
} Defensive patterns
Strategy: fallback
Validate before calling
async function headOk(url: string): Promise<boolean> {
try { const r = await fetch(url, { method: 'HEAD' }); return r.ok; } catch { return false; }
}
if (!(await headOk(url))) { /* use default mascot asset */ } Try / catch
try {
buffer = await loadRivBuffer(id, version, url);
} catch (err) {
if (err instanceof Error && err.message.includes('failed to fetch riv')) {
buffer = await loadRivBuffer(DEFAULT_ID, DEFAULT_VERSION, DEFAULT_URL);
} else throw err;
} Prevention
- Validate asset URLs against the same manifest revision that produced them.
- Cache aggressively (IndexedDB) so transient CDN failures are masked by prior fetches.
- Fall back per-mascot so one broken asset never breaks the whole mascot feature.
When it happens
Trigger: loadRivBuffer(id, version, url) with a cache miss or stale cache issues fetch(url) for the .riv asset and the CDN returns 404/403/5xx. Typical when the manifest lists an asset URL that no longer exists, the asset was renamed, the CDN deploy is partial, or an entry points at a different branch/commit that lacks the file.
Common situations: Mascot repo restructure after the manifest was cached (stale manifest + fresh assets or vice versa); GitHub raw rate limiting during development; version mismatch causing repeated cache-busting fetches of a deleted file; typo in the per-entry URL inside the manifest.
Related errors
- manifest fetch failed (${res.status})
- mascot manifest: missing mascots array
- mascot manifest: no renderable mascots
- Failed to send magic link (${response.status})
- HTTP error! status: ${response.status}
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/9ebae57a0edac126.
Report an issue: GitHub.