FlowiseAI/Flowise · error · Error

Invalid header: key must be a non-empty string

Error message

Invalid header: key must be a non-empty string

What it means

Per-entry guard: each key must be a non-empty string. Plain-object keys are always strings, so this primarily catches empty-string keys ({"": "v"}) or numeric/array-like keys that slip through when an array is passed (Object.entries on an array yields ['0', value]).

Source

Thrown at packages/components/src/headerValidation.ts:51

/**
 * Validates a set of user-supplied HTTP headers intended for outbound requests.
 * Rejects malformed keys, CRLF/control-char injection in values, hop-by-hop and
 * sensitive header names, and oversized payloads. Throws a plain Error; callers
 * are responsible for mapping to their own error types.
 */
export function validateCustomHeaders(headers: Record<string, string>): void {
    if (!headers || typeof headers !== 'object') {
        throw new Error('Invalid headers: expected an object')
    }

    const entries = Object.entries(headers)
    if (entries.length > MAX_HEADERS) {
        throw new Error(`Invalid headers: too many entries (max ${MAX_HEADERS})`)
    }

    for (const [key, value] of entries) {
        if (typeof key !== 'string' || key.length === 0) {
            throw new Error('Invalid header: key must be a non-empty string')
        }
        if (key.length > MAX_KEY_LENGTH) {
            throw new Error(`Invalid header "${key}": key exceeds ${MAX_KEY_LENGTH} chars`)
        }
        if (!RFC7230_TOKEN.test(key)) {
            throw new Error(`Invalid header "${key}": key contains illegal characters`)
        }

        const lower = key.toLowerCase()
        if (DENIED_HEADER_NAMES.has(lower) || DENIED_HEADER_PREFIXES.some((p) => lower.startsWith(p))) {
            throw new Error(`Invalid header "${key}": this header name is not allowed`)
        }

        if (typeof value !== 'string') {
            throw new Error(`Invalid header "${key}": value must be a string`)
        }
        if (value.length > MAX_VALUE_LENGTH) {
            throw new Error(`Invalid header "${key}": value exceeds ${MAX_VALUE_LENGTH} chars`)

View on GitHub (pinned to abe4a8601a)

Solutions

  1. Pass a plain object keyed by valid header names (no empty keys).
  2. Filter out empty-key entries before calling: `Object.fromEntries(Object.entries(h).filter(([k]) => k !== ''))`.
  3. If you have an array of values, map them to {key: value} pairs explicitly.

Example fix

// before
validateCustomHeaders({ '': 'no-name' }) // throws
validateCustomHeaders(['a','b']) // array, key '0' passes here but fails later

// after
validateCustomHeaders({ 'X-Trace-Id': 'a' })
Defensive patterns

Strategy: validation

Validate before calling

// Strip empty-key entries before validation
function dropEmptyKeys(headers) {
    return Object.fromEntries(Object.entries(headers).filter(([k]) => typeof k === 'string' && k.length > 0))
}

Type guard

function hasNoEmptyKeys(headers) {
    return Object.keys(headers).every((k) => typeof k === 'string' && k.length > 0)
}

Try / catch

try {
    validateCustomHeaders(headers)
} catch (e) {
    if (/key must be a non-empty string/i.test(e.message)) headers = dropEmptyKeys(headers)
    else throw e
    validateCustomHeaders(headers)
}

Prevention

When it happens

Trigger: Header bag contains an empty-string key, or an array is passed whose entries have numeric-string keys (key '0' survives non-empty but later fails RFC7230_TOKEN), or a Map/object with non-string keys was used upstream.

Common situations: Caller passed an array of values instead of an object; empty header name from user input not filtered; key/value swap left the name blank.

Related errors


AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12). Data as JSON: /api/errors/f3700f6ed269b0b6. Report an issue: GitHub.