remix-run/react-router · error · CopyTemplateError

There was a problem fetching the file from GitHub. No asset

Error message

There was a problem fetching the file from GitHub. No asset was found at that url. Please try again later.

What it means

Thrown when the GitHub Releases API returned 200 but the JSON body does not contain a usable assets array — body is falsy, not an object, lacks assets, or assets is not an Array. This is a contract violation: GitHub normally returns { assets: [...] } for a release. It typically indicates an unexpected response (a redirect HTML page captured as JSON, an upstream proxy injecting content, or a GitHub Enterprise instance with a different schema).

Source

Thrown at packages/create-react-router/copy-template.ts:244

        : `https://api.github.com/repos/${info.owner}/${info.name}/releases/tags/${info.tag}`;

    let response = await fetch(releaseUrl, { headers });

    if (response.status !== 200) {
      throw new CopyTemplateError(
        "There was a problem fetching the file from GitHub. The request " +
          `responded with a ${response.status} status. Please try again later.`,
      );
    }

    let body = (await response.json()) as { assets: GitHubApiReleaseAsset[] };
    if (
      !body ||
      typeof body !== "object" ||
      !body.assets ||
      !Array.isArray(body.assets)
    ) {
      throw new CopyTemplateError(
        "There was a problem fetching the file from GitHub. No asset was " +
          "found at that url. Please try again later.",
      );
    }

    let assetId = body.assets.find((asset) => {
      // If the release is "latest", the url won't match the download url
      return info.tag === "latest"
        ? asset?.browser_download_url?.includes(info.asset)
        : asset?.browser_download_url === tarballUrl;
    })?.id;
    if (assetId == null) {
      throw new CopyTemplateError(
        "There was a problem fetching the file from GitHub. No asset was " +
          "found at that url. Please try again later.",
      );
    }
    resourceUrl = `https://api.github.com/repos/${info.owner}/${info.name}/releases/assets/${assetId}`;

View on GitHub (pinned to 1fd704a7da)

Solutions

  1. Reproduce the request manually (curl -H 'Accept: application/vnd.github.v3+json' <releaseUrl>) and inspect the raw body to see what replaced assets.
  2. Disable any HTTP proxy or captive portal interfering with api.github.com, or allowlist it.
  3. Retry later if GitHub returned a transient malformed payload.
  4. If on GitHub Enterprise, ensure it supports the v3 releases API and returns { assets: [...] }.
  5. As a workaround, point --template at a codeload.github.com tarball of the repo instead of a release asset URL.

Example fix

// diagnose the malformed response
curl -sI -H 'Accept: application/vnd.github.v3+json' \
  https://api.github.com/repos/:owner/:repo/releases/latest
// if proxy interferes, bypass it and retry the CLI
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe that the API actually returns an assets array before invoking the CLI
async function releaseHasAssets(owner: string, repo: string, tag: string, token?: string) {
  const url = `https://api.github.com/repos/${owner}/${repo}/releases/${tag === 'latest' ? 'latest' : 'tags/' + tag}`;
  const h: HeadersInit = { Accept: 'application/vnd.github.v3+json' };
  if (token) h.Authorization = `token ${token}`;
  const r = await fetch(url, { headers: h });
  if (!r.ok) return false;
  const body = await r.json();
  return Array.isArray((body as any)?.assets);
}

Type guard

function isGitHubReleaseBody(b: unknown): b is { assets: unknown[] } {
  return typeof b === 'object' && b !== null && Array.isArray((b as any).assets);
}

Try / catch

try { await copyTemplate(url, dest, opts); }
catch (e) {
  if (e instanceof CopyTemplateError && /No asset was found/.test(e.message)
      && !/(asset)/.test(e.message)) {
    // likely a malformed/proxy response — bypass proxy or switch host
  } else throw e;
}

Prevention

When it happens

Trigger: An intercepting proxy/captive portal returns HTML with status 200; GitHub Enterprise returns a different release payload shape; the response was consumed/corrupted upstream; api.github.com returned a maintenance/migration payload that omits assets.

Common situations: Corporate proxy/MITM returning a 200 login page; CI behind a registry mirror that caches a stale or malformed response; an extremely old GitHub Enterprise version with a pre-v3 release schema despite the Accept header; network instrumentation that buffers/alters the body.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/6095592a0207cbf7. Report an issue: GitHub.