denoland/deno · error · TypeError

Blob URL fetch only supports GET method

Error message

Blob URL fetch only supports GET method

What it means

blob: URLs are served internally by fetch as a synthetic 200 response built from the Blob's stream, so only GET makes sense. The main fetch path rejects any other method (POST, PUT, HEAD, etc.) with a TypeError before creating the response. Use the Blob itself as a request body if you need to send its data somewhere.

Source

Thrown at ext/fetch/26_fetch.js:406

  // occurs. This applies to every hop, including those reached via redirects.
  //
  // Per https://fetch.spec.whatwg.org/#block-bad-port the check only applies
  // when the URL's scheme is an HTTP(S) scheme, so https bad ports (e.g.
  // https://example.com:22) are blocked too, while non-HTTP(S) schemes are
  // left alone. The spec's ALPN note covers *new* protocols negotiated over
  // TLS; it does not exempt https fetch from the list. This matches Node's
  // undici (`requestBadPort` gates on `urlIsHttpHttpsScheme`).
  const url = new URL(req.currentUrl());
  if (
    (url.protocol === "http:" || url.protocol === "https:") &&
    url.port !== "" && BAD_PORTS[url.port] === true
  ) {
    return networkError(`Requests to port ${url.port} are blocked`);
  }

  if (req.blobUrlEntry !== null) {
    if (req.method !== "GET") {
      throw new TypeError("Blob URL fetch only supports GET method");
    }

    const body = new InnerBody(req.blobUrlEntry.stream());
    terminator[abortSignal.add](() => body.error(terminator.reason));
    processUrlList(req.urlList, req.urlListProcessed);

    return {
      headerList: [
        ["content-length", String(req.blobUrlEntry.size)],
        ["content-type", req.blobUrlEntry.type],
      ],
      status: 200,
      statusMessage: "OK",
      body,
      type: "basic",
      url() {
        if (this.urlList.length == 0) return null;
        return this.urlList[this.urlList.length - 1];

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Fetch the blob: URL with GET (the default), e.g. await fetch(blobUrl)
  2. To send blob data to a server, pass the Blob as body to an http(s) URL: fetch(url, { method: "POST", body: blob })
  3. Read the underlying Blob directly instead of fetching its URL when you control the object

Example fix

// before
const res = await fetch(blobUrl, { method: "POST" }); // TypeError

// after
const res = await fetch(blobUrl); // GET is the only supported method
// or send the blob's data to a real endpoint:
const res2 = await fetch("https://api.example.com/upload", { method: "POST", body: blob });
Defensive patterns

Strategy: validation

Validate before calling

function assertBlobGet(url, init) {
  const method = (init?.method ?? "GET").toUpperCase();
  if (new URL(url).protocol === "blob:" && method !== "GET") {
    throw new Error("blob: URLs only support GET; pass the Blob as a body instead");
  }
}
assertBlobGet(blobUrl, init);
const res = await fetch(blobUrl, init);

Type guard

function isBlobUrlGet(url: string, init?: RequestInit): boolean {
  return new URL(url).protocol !== "blob:" ||
    (init?.method ?? "GET").toUpperCase() === "GET";
}

Try / catch

try {
  const res = await fetch(url, init);
} catch (err) {
  if (err instanceof TypeError && err.message.includes("Blob URL") && url.startsWith("blob:")) {
    const res = await fetch(url); // retry with GET
  } else throw err;
}

Prevention

When it happens

Trigger: fetch(`blob:${origin}/${uuid}`, { method: "POST" }) or any method other than exactly "GET" against a URL whose scheme is blob:, including method: "HEAD" and lowercase variants that do not match GET.

Common situations: Trying to 'upload' data via a blob: URL; HTTP clients or caching layers that rewrite the method on every request; code that always passes an explicit method string for uniformity.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/29509f21c2e735e3. Report an issue: GitHub.