denoland/deno · error · TypeError

Body is unusable

Error message

Body is unusable

What it means

Request.prototype.clone() must tee the body stream so both copies can be read independently. If this request's body is already disturbed (read via text()/json()/stream reader) or locked (piped/stream-locked), teeing is impossible and clone() throws TypeError "Body is unusable". Bodies are single-use; clone only works on a request whose body has never been touched.

Solutions

  1. Clone first, then read: const dup = r.clone() before any body access, and read from the duplicate
  2. Buffer the body up front (const body = await r.text()) and build two new Requests from the string
  3. For file uploads you cannot buffer, pass the underlying Blob/File as body — it is re-readable and clone-safe

Example fix

// before
const r = new Request(url, { method: "POST", body: "x" });
await r.text();
r.clone(); // throws: Body is unusable

// after
const r = new Request(url, { method: "POST", body: "x" });
const dup = r.clone(); // clone before any read
await r.text();
// dup is still fully readable
Defensive patterns

Strategy: validation

Validate before calling

if (req.bodyUsed) {
  throw new Error("Cannot clone: body already consumed — clone before reading");
}
const dup = req.clone();

Type guard

function isCloneableRequest(req: Request): boolean {
  return !req.bodyUsed;
}

Try / catch

try {
  return req.clone();
} catch (err) {
  if (err instanceof TypeError && err.message === "Body is unusable") {
    // reconstruct from buffered payload instead of cloning
    return new Request(req.url, { method: req.method, headers: [...req.headers], body: buffered });
  }
  throw err;
}

Prevention

When it happens

Trigger: const r = new Request(url, { method: "POST", body: "x" }); await r.text(); r.clone(); also r.clone() while the body is being piped to another stream.

Common situations: Clone-after-log patterns (read body for logging, then clone to forward); retry wrappers cloning a request after a failed attempt that already consumed the body; duplicate-processing pipelines.

Related errors


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

Appendix: 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 9ad36f7a2c)