denoland/deno · error · TypeError

Vary header must not contain '*'

Error message

Vary header must not contain '*'

What it means

Step 7 of Cache.put(): if the response has a Vary header whose comma-separated field list contains a trimmed '*', put() throws. A 'Vary: *' response can never be matched from cache, so storing it is pointless by definition.

Source

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

      );
    }
    if (innerRequest.method !== "GET") {
      throw new TypeError("Request method must be GET");
    }
    // Step 5.
    const innerResponse = toInnerResponse(response);
    // 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 {

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Skip caching when the vary header contains '*': check response.headers.get('vary')?.split(',').some((f) => f.trim() === '*').
  2. Server-side: stop sending 'Vary: *' for resources you want cached.

Example fix

// before
await cache.put(req, res); // res has Vary: *
// after
const vary = res.headers.get('vary') ?? '';
if (!vary.split(',').some((f) => f.trim() === '*')) {
  await cache.put(req, res);
}
Defensive patterns

Strategy: validation

Validate before calling

const vary = response.headers.get('vary') ?? '';
if (!vary.split(',').some((f) => f.trim() === '*')) {
  await cache.put(request, response);
}

Try / catch

try { await cache.put(req, res); } catch (e) { if (e instanceof TypeError) { /* uncacheable response: skip */ } else throw e; }

Prevention

When it happens

Trigger: The server sends 'Vary: *' (or a vary list containing '*') and the response is passed to cache.put().

Common situations: Servers or CDNs that emit 'Vary: *' to disable shared caching; personalized responses that client code still tries to cache.

Related errors


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