sveltejs/kit · error · Error
"${key}" header is already set
Error message
"${key}" header is already set What it means
SvelteKit stores response headers keyed by lowercase name. When you call event.setHeaders() (or set a header in an endpoint response) with a header name that maps to a lowercase key already present, it throws instead of silently overwriting — except for Server-Timing, which is appended per spec. This prevents accidental double-setting of headers like content-type or cache-control.
Source
Thrown at packages/kit/src/runtime/server/respond.js:225
setHeaders: (new_headers) => {
if (DEV) {
validateHeaders(new_headers);
}
for (const key in new_headers) {
const lower = key.toLowerCase();
const value = new_headers[key];
if (lower === 'set-cookie') {
throw new Error(
'Use `event.cookies.set(name, value, options)` instead of `event.setHeaders` to set cookies'
);
} else if (lower in headers) {
// appendHeaders-style for Server-Timing https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing
if (lower === 'server-timing') {
headers[lower] += ', ' + value;
} else {
throw new Error(`"${key}" header is already set`);
}
} else {
headers[lower] = value;
if (state.prerendering && lower === 'cache-control') {
state.prerendering.cache = /** @type {string} */ (value);
}
}
}
},
url,
isDataRequest: is_data_request,
isSubRequest: state.depth > 0,
isRemoteRequest: !!remote_id
};
// @ts-expect-error this has to be assigned lazily
event.fetch = create_fetch({View on GitHub (pinned to 03f1687fe6)
Solutions
- Find the code path setting the duplicate header (hook vs load vs endpoint) and remove one of the two setHeaders calls
- Use case-consistent naming and centralize header setting in src/hooks.server.js so it happens only once
- If appending is intended (e.g. timing), use the Server-Timing header which is concatenated, or merge values into a single comma-separated string yourself
Example fix
// before (hooks.server.js + page both set it)
export const handle = async ({ event, resolve }) => { event.setHeaders({ 'x-frame-options': 'DENY' }); return resolve(event); };
// in +page.server.js
event.setHeaders({ 'X-Frame-Options': 'DENY' });
// after
export const handle = async ({ event, resolve }) => { event.setHeaders({ 'x-frame-options': 'DENY' }); return resolve(event); };
// removed duplicate setHeaders in +page.server.js Defensive patterns
Strategy: validation
Validate before calling
function canSetHeader(headers, key) {
return !(key.toLowerCase() in headers) || key.toLowerCase() === 'server-timing';
}
// usage: if (canSetHeader(existing, 'x-frame-options')) event.setHeaders({ 'x-frame-options': 'DENY' }); Prevention
- Set global headers once in src/hooks.server.js
- Use lowercase header names consistently to spot duplicates early
- Treat Server-Timing as the only append-style header
When it happens
Trigger: Calling event.setHeaders({ 'Content-Type': 'x' }) in load and also returning a Response with a content-type header; calling setHeaders twice with the same (case-insensitive) header in a handle hook and in load; setting a header in +layout.js load and again in +page.js load.
Common situations: A handle hook adds security headers globally while a page also sets the same header; migrating an endpoint from returning a plain object with headers to setHeaders; framework upgrade made duplicate-header collisions throw instead of being ignored.
Related errors
- Cannot use `setHeaders(...)` after the response has been gen
- '${name}' cookie does not exist for ${url.pathname}, but was
- ${node.server_id}: Accessing URL properties in a promise han
- ${node.server_id}: Calling `event.fetch(...)` in a promise h
- ${node.server_id}: Calling `depends(...)` in a promise handl
AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02).
Data as JSON: /api/errors/568b16acec067797.
Report an issue: GitHub.