Budibase/budibase · error
Unexpected header format
Error message
Unexpected header format
What it means
getHeader reads a single request header from the Koa context and throws 'Unexpected header format' if Node returned an array of values instead of a string, which happens when a header is sent multiple times in one request.
Source
Thrown at packages/backend-core/src/middleware/authenticated.ts:103
if (userId) {
return {
valid: true,
user: await getUser({
userId,
tenantId,
populateUser,
}),
}
} else {
throw new InvalidAPIKeyWarning()
}
})
}
function getHeader(ctx: Ctx, header: Header): string | undefined {
const contents = ctx.request.headers[header]
if (Array.isArray(contents)) {
throw new Error("Unexpected header format")
}
return contents
}
/**
* This middleware is tenancy aware, so that it does not depend on other middlewares being used.
* The tenancy modules should not be used here and it should be assumed that the tenancy context
* has not yet been populated.
*/
export function authenticated(
noAuthPatterns: EndpointMatcher[] = [],
opts: { publicAllowed?: boolean; populateUser?: Function } = {
publicAllowed: false,
}
) {
const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : []
return (async (ctx: Ctx, next: Next) => {
let publicEndpoint = falseView on GitHub (pinned to a81a902e9a)
Solutions
- Fix the client to send the header exactly once (remove duplicate interceptor/default header)
- Configure the proxy to overwrite instead of append duplicate headers
- Deduplicate the raw header before middleware runs
- Use a different, singular header name to avoid collisions with existing defaults
Example fix
// before
fetch(url, { headers: { Authorization: `Bearer ${t}` } }) // interceptor also adds Authorization
// after
client.defaults.headers.common.Authorization = undefined
fetch(url, { headers: { Authorization: `Bearer ${t}` } }) Defensive patterns
Strategy: type-guard
Validate before calling
const h = req.headers["authorization"]
if (Array.isArray(h)) throw new Error(`Duplicate authorization header: ${h.length} values`) Type guard
function singleHeader(v: string | string[] | undefined): string | undefined {
return Array.isArray(v) ? v[0] : v
} Try / catch
try {
return getHeader(ctx, Header.API_KEY)
} catch (e) {
if (e.message === "Unexpected header format") {
console.warn("Client sent duplicate header; taking first value")
return (ctx.request.headers[header] as string[])[0]
}
throw e
} Prevention
- Audit clients/proxies for duplicate header injection
- Avoid adding the same header in both a default and a per-request layer
- Deduplicate headers at the reverse proxy (nginx: proxy_set_header once)
When it happens
Trigger: A client (or proxy) sends the same header twice, e.g. duplicate Authorization or api-key headers, causing ctx.request.headers[header] to be string[].
Common situations: Reverse proxies appending rather than overwriting headers; HTTP/2 clients and middleware that join headers; fetch wrappers adding Authorization twice (manual + interceptor); curl -H passed twice.
Related errors
- App not found
- OIDC Config contents invalid
- Koa context must be supplied to logout.
- invalid_api_key
- Unexpected response when fetching openid-configuration: ${re
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/01c78e3acc25fc26.
Report an issue: GitHub.