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
- Clone before reading: await cache.put(req, response.clone()), then read the original.
- Cache first and read via cache.match afterwards.
- 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
- Clone at the point of caching: cache.put(req, res.clone()) — never read and cache the same object.
- Check response.bodyUsed as a cheap pre-condition before put().
- Structure fetch wrappers as: clone for cache, return original to caller.
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
- Request cache mode "only-if-cached" can only be used with sa
- Input request's body is unusable
- Body is unusable
- Body is unusable
- BenchContext::end() has already been invoked
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/2d90a3036614a59b.
Report an issue: GitHub.