sveltejs/kit · error · Error

`cache-control` header contains empty directives. ${error_su

Error message

`cache-control` header contains empty directives. ${error_suffix}

What it means

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.

Source

Thrown at packages/kit/src/runtime/server/validate-headers.js:29

	'immutable',
	'stale-while-revalidate',
	'stale-if-error',
	'no-transform',
	'only-if-cached',
	'max-stale',
	'min-fresh'
]);

const CONTENT_TYPE_PATTERN =
	/^(application|audio|example|font|haptics|image|message|model|multipart|text|video|x-[a-z]+)\/[-+.\w]+$/i;

/** @type {Record<string, (value: string) => void>} */
const HEADER_VALIDATORS = {
	'cache-control': (value) => {
		const error_suffix = `(While parsing "${value}".)`;
		const parts = value.split(',').map((part) => part.trim());
		if (parts.some((part) => !part)) {
			throw new Error(`\`cache-control\` header contains empty directives. ${error_suffix}`);
		}

		const directives = parts.map((part) => part.split('=')[0].toLowerCase());
		const invalid = directives.find((directive) => !VALID_CACHE_CONTROL_DIRECTIVES.has(directive));
		if (invalid) {
			throw new Error(
				`Invalid cache-control directive "${invalid}". Did you mean one of: ${[...VALID_CACHE_CONTROL_DIRECTIVES].join(', ')}? ${error_suffix}`
			);
		}
	},

	'content-type': (value) => {
		const type = value.split(';')[0].trim();
		const error_suffix = `(While parsing "${value}".)`;
		if (!CONTENT_TYPE_PATTERN.test(type)) {
			throw new Error(`Invalid content-type value "${type}". ${error_suffix}`);
		}
	}

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Remove the empty directive — check for trailing commas and empty interpolated variables
  2. Guard the value before setting: only include directives whose variables are truthy
  3. Validate the final string with a quick split(',') check in dev before shipping

Example fix

// before
event.setHeaders({ 'cache-control': `max-age=${ttl}, immutable` }); // ttl = ''
// after
event.setHeaders({ 'cache-control': ttl ? `max-age=${ttl}, immutable` : 'immutable' });
Defensive patterns

Strategy: validation

Validate before calling

function hasEmptyCacheDirectives(value) {
  return value.split(',').some((p) => !p.trim());
}
// if (hasEmptyCacheDirectives(cc)) throw new Error('bad cache-control: ' + cc);

Try / catch

try {
  event.setHeaders({ 'cache-control': cc });
} catch (e) {
  if (e.message.includes('empty directives')) console.error('Fix cache-control value:', cc);
}

Prevention

When it happens

Trigger: setHeaders({ 'cache-control': 'public, , max-age=60' }) — a trailing comma like 'no-store,' or a manually-built string with empty segments.

Common situations: Template-string construction of cache-control where a variable is empty: `max-age=${ttl}, immutable` with ttl='' ; copy-pasted headers with stray commas.

Related errors


AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02). Data as JSON: /api/errors/a979ab2aea0b7258. Report an issue: GitHub.