google/zx · error · Fail

Failed to fetch remote script: ${remote} (${res.status})

Error message

Failed to fetch remote script: ${remote} (${res.status})

What it means

Thrown by readScriptFromHttp() when `zx <http(s) url>` gets a non-2xx response. zx fetches the URL with the global fetch() and rejects on !res.ok, embedding the HTTP status code in the message and setting exitCode to 1.

Source

Thrown at src/cli.ts:256

  if (ext === '.md') {
    script = transformMarkdown(script)
    tempPath = getFilepath(dir, base, EXT)
  }
  if (argSlice) updateArgv(argv._.slice(argSlice))

  return { script, scriptPath, tempPath }
}

async function readScriptFromStdin(): Promise<string> {
  return process.stdin.isTTY ? '' : stdin()
}

async function readScriptFromHttp(remote: string): Promise<string> {
  const res = await fetch(remote)
  if (!res.ok) {
    console.error(`Error: Can't get ${remote}`)
    process.exitCode = 1
    throw new Fail(`Failed to fetch remote script: ${remote} (${res.status})`)
  }
  return res.text()
}

export function injectGlobalRequire(origin: string): void {
  const __filename = path.resolve(origin)
  const __dirname = path.dirname(__filename)
  const require = createRequire(origin)
  Object.assign(globalThis, { __filename, __dirname, require })
}

export function isMain(
  meta: ImportMeta['url'] | ImportMeta = import.meta.url,
  scriptpath: string = process.argv[1]
): boolean {
  if (typeof meta === 'string') {
    if (meta.startsWith('file:')) {
      const modulePath = url.fileURLToPath(meta).replace(/\.\w+$/, '')

View on GitHub (pinned to 00a2c484e2)

Solutions

  1. Verify the URL independently: `curl -iL <url>` and check the status.
  2. Use the raw URL for gists/repos (e.g. `https://raw.githubusercontent.com/...`).
  3. Add authentication (token in the URL or a custom fetch via a wrapper) if the resource is private.
  4. Download the file and run it locally instead: `curl -o s.mjs <url> && zx s.mjs`.
  5. Retry; if flaky, wrap the zx invocation in a retry or check connectivity/proxy.

Example fix

// before: gist HTML page is not the script
$ zx https://gist.github.com/user/abc123
// after: use the raw endpoint
$ zx https://raw.githubusercontent.com/gist/abc123/raw/script.mjs
Defensive patterns

Strategy: retry

Validate before calling

async function headOk(url: string): Promise<boolean> {
  const res = await fetch(url, { method: 'HEAD' })
  return res.ok
}

if (!(await headOk(scriptUrl))) {
  throw new Error(`remote script unreachable: ${scriptUrl}`)
}

Try / catch

try {
  await runRemote(scriptUrl)
} catch (e) {
  if (e instanceof Fail && /Failed to fetch remote script/.test(e.message)) {
    // fall back: download then run locally
    await $`curl -fsSL ${scriptUrl} -o /tmp/s.mjs`
    await $`zx /tmp/s.mjs`
  } else throw e
}

Prevention

When it happens

Trigger: `zx https://example.com/script.mjs` where the server returns 404/403/500; a gist URL that is not the raw endpoint (HTML page, non-2xx); a private repo/gist requiring auth; corporate proxy returning a block page (403).

Common situations: Wrong or mistyped URL; deleted/moved remote script; auth required; corporate proxy/VPN interfering; transient server outage.

Related errors


AI-assisted analysis of google/zx@00a2c484e2 (2026-08-13). Data as JSON: /api/errors/b8c805003627d6f9. Report an issue: GitHub.