moeru-ai/airi · error · Error

Failed to fetch MMD model: ${response.status} ${response.sta

Error message

Failed to fetch MMD model: ${response.status} ${response.statusText}

What it means

`loadMMDModelFromSource(src, options)` fetches the MMD model bytes via `fetch(src)`. If the response is not OK (`!response.ok`, i.e. HTTP status outside 200–299) it throws `Failed to fetch MMD model: <status> <statusText>`. The OPFSCache path is only taken when a cache hit exists, so a network/origin error surfaces here.

Source

Thrown at packages/stage-ui-mmd/src/utils/mmd-loader.ts:96

 *
 * Accepts either a packaged ZIP (the usual distribution form: model plus
 * textures) or a bare `.pmx`/`.pmd` URL. ZIP archives are unpacked to blob
 * URLs and a basename-based texture resolver is installed on the loader; raw
 * URLs are loaded directly and rely on the server's relative paths.
 *
 * The returned `dispose()` revokes any blob URLs created during the load. It
 * does not dispose the mesh's GPU resources — the scene owns that lifecycle.
 */
export async function loadMMDModelFromSource(src: string, options: LoadMMDOptions = {}): Promise<ResolvedMMDModel> {
  const cachedSource = options.cacheKey ? await OPFSCache.get(options.cacheKey, src) : null
  let buffer: ArrayBuffer
  if (cachedSource) {
    buffer = await cachedSource.arrayBuffer()
  }
  else {
    const response = await fetch(src)
    if (!response.ok)
      throw new Error(`Failed to fetch MMD model: ${response.status} ${response.statusText}`)
    buffer = await response.arrayBuffer()
  }

  if (isZip(buffer)) {
    const assets = await loadMMDZip(buffer)
    let mmd: MMD | undefined
    try {
      const { loader, manager } = createMMDLoaderContext(assets.urlModifier)
      mmd = await loadMMD(loader, assets.modelBlobUrl)
      prepareMMDMaterials(mmd.mesh)
      if (options.waitForTextures)
        await waitForManagerIdle(manager)
      if (options.cacheKey && !cachedSource)
        await OPFSCache.save(options.cacheKey, new Blob([buffer]), src)
      return {
        mmd,
        mesh: mmd.mesh,
        format: assets.variant.format,

View on GitHub (pinned to 27111382b4)

Solutions

  1. Verify the `src` URL resolves in a browser/curl and returns the expected `.pmx`/`.pmd`/`.zip` bytes with HTTP 200.
  2. Add CORS headers (`Access-Control-Allow-Origin`) on the hosting origin for the model path.
  3. Provide a working `options.cacheKey` so a previously cached copy is used when the network fails.
  4. Catch the error and offer a retry or a fallback model URL.

Example fix

// before
const mmd = await loadMMDModelFromSource(src)

// after
const res = await fetch(src, { method: 'HEAD' }).catch(() => null)
if (!res || !res.ok) throw new Error(`MMD source unreachable at ${src} (HTTP ${res?.status ?? 'no response'})`)
const mmd = await loadMMDModelFromSource(src, { cacheKey: `mmd:${src}` })
Defensive patterns

Strategy: retry

Validate before calling

async function isMMDSrcReachable(src: string): Promise<boolean> {
  try {
    const r = await fetch(src, { method: 'HEAD' })
    return r.ok
  } catch { return false }
}

if (await isMMDSrcReachable(src)) await loadMMDModelFromSource(src, { cacheKey: `mmd:${src}` })

Try / catch

let lastErr: unknown
for (const url of [src, fallbackSrc]) {
  try {
    return await loadMMDModelFromSource(url, { cacheKey: `mmd:${url}` })
  } catch (e) {
    lastErr = e
    if (!(e instanceof Error && e.message.startsWith('Failed to fetch MMD model'))) throw e
  }
}
throw lastErr

Prevention

When it happens

Trigger: A `src` URL that returns 404 (model not found), 403 (auth/CORS), 500 (server error), a wrong base URL, a CDN path typo, or a cross-origin resource without proper CORS headers returning an opaque/error response. Also a server temporarily down returning 502/503.

Common situations: Wrong asset URL after a deploy/rename; missing CORS configuration on the model host; expired signed URL; the model file was never uploaded; reverse proxy returning HTML error pages with HTTP 200 (those slip through but fail later) vs a real non-2xx.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/b61c3cd09293ea1a. Report an issue: GitHub.