denoland/deno · error · TypeError

Response body is already used

Error message

Response body is already used

What it means

Step 8 of Cache.put(): the cache consumes the response body itself, so the body must be unused. If the stream is already read or locked (innerResponse.body.unusable()), put() throws 'Response body is already used'.

Source

Thrown at ext/cache/01_cache.js:176

    // Step 6.
    if (innerResponse.status === 206) {
      throw new TypeError("Response status must not be 206");
    }
    // Step 7.
    const varyHeader = getHeader(innerResponse.headerList, "vary");
    if (varyHeader) {
      const fieldValues = StringPrototypeSplit(varyHeader, ",");
      for (let i = 0; i < fieldValues.length; ++i) {
        const field = fieldValues[i];
        if (StringPrototypeTrim(field) === "*") {
          throw new TypeError("Vary header must not contain '*'");
        }
      }
    }

    // Step 8.
    if (innerResponse.body !== null && innerResponse.body.unusable()) {
      throw new TypeError("Response body is already used");
    }

    const stream = innerResponse.body?.stream;
    let rid = null;
    if (stream) {
      const resourceBacking = getReadableStreamResourceBacking(
        innerResponse.body?.stream,
      );
      if (resourceBacking) {
        rid = resourceBacking.rid;
      } else {
        rid = resourceForReadableStream(stream, innerResponse.body?.length);
      }
    }

    // Remove fragment from request URL before put.
    reqUrl.hash = "";

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Clone before reading: await cache.put(req, response.clone()), then read the original.
  2. Cache first and read via cache.match afterwards.
  3. Build a fresh Response from data you already have: new Response(text, init).

Example fix

// before
const text = await res.text();
await cache.put(req, res); // throws: body already used
// after
await cache.put(req, res.clone());
const text = await res.text();
Defensive patterns

Strategy: validation

Validate before calling

if (!response.bodyUsed) {
  await cache.put(request, response.clone());
  return response;
}
throw new Error('cannot cache a response whose body was read');

Try / catch

try { await cache.put(req, res); } catch (e) { if (e instanceof TypeError && /already used/.test(e.message)) { /* re-fetch or rebuild from stored text */ } else throw e; }

Prevention

When it happens

Trigger: await response.text()/.json()/.arrayBuffer() before cache.put(request, response); passing a response whose body was locked or read by another consumer.

Common situations: Logging or inspecting a fetched response and then trying to cache the same object; caching after forwarding the body to another reader.

Related errors


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