{"record":{"id":"addf82a81f562d19","repo":"denoland/deno","slug":"input-request-s-body-is-unusable","errorCode":null,"errorMessage":"Input request's body is unusable","messagePattern":"Input request's body is unusable","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/23_request.js","lineNumber":585,"sourceCode":"    // 37.\n    if (init.body !== undefined && init.body !== null) {\n      const res = extractBody(init.body);\n      initBody = res.body;\n      if (res.contentType !== null && !this[_headers].has(\"content-type\")) {\n        this[_headers].append(\"Content-Type\", res.contentType);\n      }\n    }\n\n    // 38.\n    const inputOrInitBody = initBody ?? inputBody;\n\n    // 40.\n    let finalBody = inputOrInitBody;\n\n    // 41.\n    if (initBody === null && inputBody !== null) {\n      if (input[_body] && input[_body].unusable()) {\n        throw new TypeError(\"Input request's body is unusable\");\n      }\n      finalBody = inputBody.createProxy();\n    }\n\n    // 42.\n    request.body = finalBody;\n  }\n\n  get method() {\n    webidl.assertBranded(this, RequestPrototype);\n    if (this[_method]) {\n      return this[_method];\n    }\n    this[_method] = this[_request].method;\n    return this[_method];\n  }\n\n  get url() {","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/23_request.js#L567-L603","documentation":"Request constructor step 41: when init.body is absent but the input Request has a body, the input body is proxied into the new request — but only if it is still usable. input[_body].unusable() returns true once the stream is disturbed or locked (already read, tee'd, or piped), and then TypeError 'Input request's body is unusable' is thrown.","triggerScenarios":"const reqB = new Request(reqA) after reqA's body was already read (reqA.text(), reqA.json(), iterated, or piped through a stream).","commonSituations":"Middleware that inspects a body (await req.text()) and then constructs a derived Request for forwarding; logging proxies that consume and re-wrap requests; retry wrappers around an already-read request.","solutions":["Read the body once into memory and rebuild: new Request(url, { method, headers, body: await input.text() })","Clone BEFORE reading: const copy = input.clone(); const text = await input.text(); — then reuse copy","In middleware, buffer the body first and pass the buffer to downstream handlers"],"exampleFix":"// before\nconst bodyText = await input.text(); // input now unusable\nconst forwarded = new Request(target, input); // TypeError\n\n// after\nconst bodyText = await input.text();\nconst forwarded = new Request(target, {\n  method: input.method,\n  headers: input.headers,\n  body: bodyText,\n});","handlingStrategy":"validation","validationCode":"async function rewrapRequest(input, targetUrl) {\n  // Buffer once; safe even if already partially handled upstream\n  const bodyText = await input.clone().text(); // clone guards against double-read below\n  return new Request(targetUrl, {\n    method: input.method,\n    headers: input.headers,\n    body: ['GET', 'HEAD'].includes(input.method) ? undefined : bodyText,\n  });\n}","typeGuard":null,"tryCatchPattern":"try {\n  derived = new Request(target, input);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes('body is unusable')) {\n    derived = new Request(target, { method: input.method, headers: input.headers, body: bufferedBody });\n  } else throw err;\n}","preventionTips":["Clone an input Request before reading its body, then derive from the clone","Buffer bodies in middleware and forward the buffered value","Never construct derived Requests from a body you have already consumed"],"tags":["fetch","request","body","stream"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}