{"record":{"id":"abe3287dd6ef37fc","repo":"denoland/deno","slug":"body-already-consumed","errorCode":null,"errorMessage":"Body already consumed","messagePattern":"Body already consumed","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/22_body.js","lineNumber":189,"sourceCode":"   */\n  consumed() {\n    if (\n      ObjectPrototypeIsPrototypeOf(\n        ReadableStreamPrototype,\n        this.streamOrStatic,\n      )\n    ) {\n      return isReadableStreamDisturbed(this.streamOrStatic);\n    }\n    return this.streamOrStatic.consumed;\n  }\n\n  /**\n   * https://fetch.spec.whatwg.org/#concept-body-consume-body\n   * @returns {Promise<Uint8Array>}\n   */\n  consume() {\n    if (this.unusable()) throw new TypeError(\"Body already consumed\");\n    if (\n      ObjectPrototypeIsPrototypeOf(\n        ReadableStreamPrototype,\n        this.streamOrStatic,\n      )\n    ) {\n      readableStreamThrowIfErrored(this.stream);\n      return PromisePrototypeCatch(\n        readableStreamCollectIntoUint8Array(this.stream),\n        (e) => {\n          if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, e)) {\n            // TODO(kt3k): We probably like to pass e as `cause` if BadResource supports it.\n            throw new e.constructor(\n              \"Cannot read body as underlying resource unavailable\",\n            );\n          }\n          throw e;\n        },","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/22_body.js#L171-L207","documentation":"InnerBody.consume() in ext/fetch/22_body.js backs res.text(), res.json(), res.arrayBuffer(), res.blob(), res.bytes() and res.formData(). It throws TypeError \"Body already consumed\" when unusable(): the underlying ReadableStream is locked or disturbed (a reader was acquired, or it was piped/read), or a static body is already marked consumed. A body can be consumed exactly once.","triggerScenarios":"await res.text() twice on the same response; res.json() after const reader = res.body.getReader(); calling res.formData() after piping res.body elsewhere.","commonSituations":"Logging middleware that reads the body for logging and then passing the same response to business code; retry logic that re-parses a response; caching layers that hold the Response object instead of its parsed content.","solutions":["Check res.bodyUsed before any read and parse once, storing the result","Call res.clone() before the first read when two consumers need the body","In middleware, buffer the bytes: const buf = await res.clone().arrayBuffer() and pass the original on, or construct a fresh Response from the buffered bytes"],"exampleFix":"// before\nconst a = await res.json();\nconst b = await res.json(); // TypeError: Body already consumed\n\n// after\nconst data = await res.json();\nconst a = data;\nconst b = data;\n// or: const [a, b] = await Promise.all([res.clone().json(), res.json()]);","handlingStrategy":"validation","validationCode":"if (res.bodyUsed) {\n  throw new Error(\"body already read - reuse the cached parsed value\");\n}\nconst data = await res.json();","typeGuard":"function hasUnreadBody(res: Response | Request): boolean {\n  return !res.bodyUsed && !(res.body?.locked ?? false);\n}","tryCatchPattern":"try { return await res.json(); } catch (e) {\n  if (e instanceof TypeError && e.message === \"Body already consumed\") {\n    throw new Error(\"duplicate body read; cache the first parse or clone() before reading\");\n  }\n  throw e;\n}","preventionTips":["Parse a body once and pass the parsed value downstream","Call res.clone() before the first read when logging middleware also needs the body","Store parsed data (not the Response) in caches"],"tags":["fetch","body","streams","async"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}