{"record":{"id":"4f4cbf1d9ac598d6","repo":"denoland/deno","slug":"vary-header-must-not-contain","errorCode":null,"errorMessage":"Vary header must not contain '*'","messagePattern":"Vary header must not contain '\\*'","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/cache/01_cache.js","lineNumber":169,"sourceCode":"      );\n    }\n    if (innerRequest.method !== \"GET\") {\n      throw new TypeError(\"Request method must be GET\");\n    }\n    // Step 5.\n    const innerResponse = toInnerResponse(response);\n    // Step 6.\n    if (innerResponse.status === 206) {\n      throw new TypeError(\"Response status must not be 206\");\n    }\n    // Step 7.\n    const varyHeader = getHeader(innerResponse.headerList, \"vary\");\n    if (varyHeader) {\n      const fieldValues = StringPrototypeSplit(varyHeader, \",\");\n      for (let i = 0; i < fieldValues.length; ++i) {\n        const field = fieldValues[i];\n        if (StringPrototypeTrim(field) === \"*\") {\n          throw new TypeError(\"Vary header must not contain '*'\");\n        }\n      }\n    }\n\n    // Step 8.\n    if (innerResponse.body !== null && innerResponse.body.unusable()) {\n      throw new TypeError(\"Response body is already used\");\n    }\n\n    const stream = innerResponse.body?.stream;\n    let rid = null;\n    if (stream) {\n      const resourceBacking = getReadableStreamResourceBacking(\n        innerResponse.body?.stream,\n      );\n      if (resourceBacking) {\n        rid = resourceBacking.rid;\n      } else {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/cache/01_cache.js#L151-L187","documentation":"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.","triggerScenarios":"The server sends 'Vary: *' (or a vary list containing '*') and the response is passed to cache.put().","commonSituations":"Servers or CDNs that emit 'Vary: *' to disable shared caching; personalized responses that client code still tries to cache.","solutions":["Skip caching when the vary header contains '*': check response.headers.get('vary')?.split(',').some((f) => f.trim() === '*').","Server-side: stop sending 'Vary: *' for resources you want cached."],"exampleFix":"// before\nawait cache.put(req, res); // res has Vary: *\n// after\nconst vary = res.headers.get('vary') ?? '';\nif (!vary.split(',').some((f) => f.trim() === '*')) {\n  await cache.put(req, res);\n}","handlingStrategy":"validation","validationCode":"const vary = response.headers.get('vary') ?? '';\nif (!vary.split(',').some((f) => f.trim() === '*')) {\n  await cache.put(request, response);\n}","typeGuard":null,"tryCatchPattern":"try { await cache.put(req, res); } catch (e) { if (e instanceof TypeError) { /* uncacheable response: skip */ } else throw e; }","preventionTips":["Inspect the vary header before caching responses from servers you do not control.","In tests, avoid mocking responses with 'Vary: *' unless you assert the cache rejects them."],"tags":["cache","http-headers","vary","service-worker"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}