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 = false

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Fix the client to send the header exactly once (remove duplicate interceptor/default header)
  2. Configure the proxy to overwrite instead of append duplicate headers
  3. Deduplicate the raw header before middleware runs
  4. 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

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


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/01c78e3acc25fc26. Report an issue: GitHub.