remix-run/react-router · error · CopyTemplateError

There was a problem extracting the file from the provided…

Error message

There was a problem extracting the file from the provided template.  Template URL: `${tarballUrl}`  Destination directory: `${downloadPath}`

What it means

After a successful HTTP 200 download, the gzip/tar extraction pipeline threw while streaming the response body into the destination. The body was not a valid gzipped tarball: an HTML error page served under a 200 (SPA catch-all hosting), a tarball compressed with something other than gzip (zstd/xz), or a stream interrupted mid-transfer.

Solutions

  1. Verify the URL serves real gzip data: curl -sL <url> | file - should report 'gzip compressed data'
  2. Re-pack the archive as gzip-compressed tar and re-host it
  3. If behind a proxy, bypass it or configure it correctly, then retry
  4. Re-run with --debug to see which stream stage failed

Example fix

# before: SPA host serves index.html for the tarball path
npx create-react-router my-app --template https://tpl.example.com/template.tar.gz

# after: confirm and fix hosting so the URL serves gzip
curl -sL https://tpl.example.com/template.tar.gz | file -
# -> 'gzip compressed data' (not HTML document)
Defensive patterns

Strategy: try-catch

Validate before calling

async function urlServesGzipTarball(url: string): Promise<boolean> {
  const res = await fetch(url);
  if (!res.ok) return false;
  const buf = Buffer.from(await res.arrayBuffer());
  // gzip magic number 0x1f 0x8b
  return buf.length > 2 && buf[0] === 0x1f && buf[1] === 0x8b;
}

if (!(await urlServesGzipTarball(templateUrl))) {
  throw new Error("Template URL does not serve gzip data (check hosting/catch-all routes)");
}

Try / catch

try {
  await copyTemplate(templateUrl, dest, { onError: (e) => { throw e; } });
} catch (e) {
  if (e instanceof CopyTemplateError && /problem extracting the file from the provided template/.test(e.message)
      && /Template URL:/.test(e.message)) {
    console.error("Downloaded body was not a valid gzipped tarball — verify the URL with curl.");
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: A template URL on a static host with a catch-all route returning index.html for unknown paths; proxy/MITM mangling the body; network reset during streaming; archives packed with non-gzip compression renamed to .tar.gz.

Common situations: Self-hosted template tarballs behind SPA rewrites; corporate proxies corrupting binary transfers; flaky CI networks truncating downloads.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/f417b57905779436. Report an issue: GitHub.

Appendix: source

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

              filePathHasFiles = true;
              header.name = header.name.replace(filePath, "");
            } else {
              header.name = "__IGNORE__";
            }
          }

          return header;
        },
        ignore(_filename, header) {
          if (!header) {
            throw Error("Header is undefined");
          }
          return header.name === "__IGNORE__";
        },
      }),
    );
  } catch {
    throw new CopyTemplateError(
      "There was a problem extracting the file from the provided template." +
        `  Template URL: \`${tarballUrl}\`` +
        `  Destination directory: \`${downloadPath}\``,
    );
  }

  if (filePath && !filePathHasFiles) {
    throw new CopyTemplateError(
      `The path "${filePath}" was not found in this ${
        isGithubUrl ? "GitHub repo." : "tarball."
      }`,
    );
  }
}

// Copied from react-router-node/stream.ts
async function writeReadableStreamToWritable(
  stream: ReadableStream,

View on GitHub (pinned to 6beaca3952)