{"record":{"id":"a979ab2aea0b7258","repo":"sveltejs/kit","slug":"cache-control-header-contains-empty-directives","errorCode":null,"errorMessage":"`cache-control` header contains empty directives. ${error_suffix}","messagePattern":"`cache-control` header contains empty directives\\. (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/kit/src/runtime/server/validate-headers.js","lineNumber":29,"sourceCode":"\t'immutable',\n\t'stale-while-revalidate',\n\t'stale-if-error',\n\t'no-transform',\n\t'only-if-cached',\n\t'max-stale',\n\t'min-fresh'\n]);\n\nconst CONTENT_TYPE_PATTERN =\n\t/^(application|audio|example|font|haptics|image|message|model|multipart|text|video|x-[a-z]+)\\/[-+.\\w]+$/i;\n\n/** @type {Record<string, (value: string) => void>} */\nconst HEADER_VALIDATORS = {\n\t'cache-control': (value) => {\n\t\tconst error_suffix = `(While parsing \"${value}\".)`;\n\t\tconst parts = value.split(',').map((part) => part.trim());\n\t\tif (parts.some((part) => !part)) {\n\t\t\tthrow new Error(`\\`cache-control\\` header contains empty directives. ${error_suffix}`);\n\t\t}\n\n\t\tconst directives = parts.map((part) => part.split('=')[0].toLowerCase());\n\t\tconst invalid = directives.find((directive) => !VALID_CACHE_CONTROL_DIRECTIVES.has(directive));\n\t\tif (invalid) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid cache-control directive \"${invalid}\". Did you mean one of: ${[...VALID_CACHE_CONTROL_DIRECTIVES].join(', ')}? ${error_suffix}`\n\t\t\t);\n\t\t}\n\t},\n\n\t'content-type': (value) => {\n\t\tconst type = value.split(';')[0].trim();\n\t\tconst error_suffix = `(While parsing \"${value}\".)`;\n\t\tif (!CONTENT_TYPE_PATTERN.test(type)) {\n\t\t\tthrow new Error(`Invalid content-type value \"${type}\". ${error_suffix}`);\n\t\t}\n\t}","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/sveltejs/kit/blob/03f1687fe612ce3d2d9131139b5b188d9cf90c64/packages/kit/src/runtime/server/validate-headers.js#L11-L47","documentation":"SvelteKit validates cache-control headers set via setHeaders during prerendering. A directive like 'max-age=3600, , private' (empty string between commas) is malformed, so it throws with the offending value to surface the bug early.","triggerScenarios":"setHeaders({ 'cache-control': 'public, , max-age=60' }) — a trailing comma like 'no-store,' or a manually-built string with empty segments.","commonSituations":"Template-string construction of cache-control where a variable is empty: `max-age=${ttl}, immutable` with ttl='' ; copy-pasted headers with stray commas.","solutions":["Remove the empty directive — check for trailing commas and empty interpolated variables","Guard the value before setting: only include directives whose variables are truthy","Validate the final string with a quick split(',') check in dev before shipping"],"exampleFix":"// before\nevent.setHeaders({ 'cache-control': `max-age=${ttl}, immutable` }); // ttl = ''\n// after\nevent.setHeaders({ 'cache-control': ttl ? `max-age=${ttl}, immutable` : 'immutable' });","handlingStrategy":"validation","validationCode":"function hasEmptyCacheDirectives(value) {\n  return value.split(',').some((p) => !p.trim());\n}\n// if (hasEmptyCacheDirectives(cc)) throw new Error('bad cache-control: ' + cc);","typeGuard":null,"tryCatchPattern":"try {\n  event.setHeaders({ 'cache-control': cc });\n} catch (e) {\n  if (e.message.includes('empty directives')) console.error('Fix cache-control value:', cc);\n}","preventionTips":["Filter out falsy interpolated values before building the header","Join directives with .filter(Boolean).join(', ')","Lint for hand-built cache-control template strings"],"tags":["cache-control","http-headers","validation"],"backgroundTag":"invalid-cache-control-header","analyzedSha":"03f1687fe612ce3d2d9131139b5b188d9cf90c64","analyzedAt":"2026-09-02T02:01:50.504Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}