denoland/deno · error · TypeError

Body is unusable

Error message

Body is unusable

What it means

Request.prototype.clone() checks this[_body].unusable() first: if the body stream is already disturbed or locked, clone() throws TypeError 'Body is unusable'. Cloning must tee the underlying stream, which is only possible before anything has read it. The message has the prefix 'Failed to execute Request.clone' in the error cause chain of the outer DOMException.

Source

Thrown at ext/fetch/23_request.js:674

    }
    return referrer;
  }

  get referrerPolicy() {
    webidl.assertBranded(this, RequestPrototype);
    return this[_request].referrerPolicy ?? "";
  }

  get signal() {
    webidl.assertBranded(this, RequestPrototype);
    return this[_signal];
  }

  clone() {
    const prefix = "Failed to execute 'Request.clone'";
    webidl.assertBranded(this, RequestPrototype);
    if (this[_body] && this[_body].unusable()) {
      throw new TypeError("Body is unusable");
    }
    const clonedReq = cloneInnerRequest(this[_request]);

    const materializedSignal = this[_signal];
    const clonedSignal = createDependentAbortSignal(
      [materializedSignal],
      prefix,
    );

    const request = new Request(_brand);
    request[_request] = clonedReq;
    request[_signalCache] = clonedSignal;
    headerListFromHeaders(this[_headers]);
    request[_headersGuard] = guardFromHeaders(this[_headers]);
    return request;
  }

  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Clone before first read: const copy = req.clone(); then read one of them
  2. If already read, reconstruct a new Request from the buffered body string/bytes instead of cloning
  3. Read via req.blob()/text() once and pass the materialized value to all consumers

Example fix

// before
const data = await req.text();
const copy = req.clone(); // throws: body disturbed

// after
const copy = req.clone(); // tee while unused
const data = await req.text();
// copy still has an independent unread body
Defensive patterns

Strategy: validation

Validate before calling

function cloneOrRebuild(req, bodyTextIfRead) {
  try {
    return req.clone();
  } catch {
    return new Request(req.url, {
      method: req.method,
      headers: req.headers,
      body: bodyTextIfRead,
    });
  }
}

Try / catch

try {
  copy = req.clone();
} catch (err) {
  if (err instanceof TypeError && /Body is unusable|Failed to execute 'Request.clone'/.test(String(err.cause ?? err.message))) {
    copy = new Request(req.url, { method: req.method, headers: req.headers, body: buffered });
  } else throw err;
}

Prevention

When it happens

Trigger: const r = new Request(url, { method: 'POST', body: 'x' }); await r.text(); r.clone() — any read, iteration, or pipe of the body before clone().

Common situations: Retry/middleware code that clones a request after logging its body; clone-then-read ordering bugs in async handlers where an earlier await consumed the stream.

Related errors


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