nodejs/node · error · TypeError

key must be ascii string

Error message

key must be ascii string

What it means

Thrown by the TstNode constructor (the ternary search tree undici uses to index HTTP header field-names) when key.charCodeAt(index) > 0x7F. Per RFC 7230 §3.2.6, header field-names are restricted to the ASCII token grammar, so any non-ASCII byte in a name is rejected when the tree node is built.

Source

Thrown at deps/undici/src/lib/core/tree.js:31

  /** @type {null | TstNode} */
  middle = null
  /** @type {null | TstNode} */
  right = null
  /** @type {number} */
  code
  /**
   * @param {string} key
   * @param {any} value
   * @param {number} index
   */
  constructor (key, value, index) {
    if (index === undefined || index >= key.length) {
      throw new TypeError('Unreachable')
    }
    const code = this.code = key.charCodeAt(index)
    // check code is ascii string
    if (code > 0x7F) {
      throw new TypeError('key must be ascii string')
    }
    if (key.length !== ++index) {
      this.middle = new TstNode(key, value, index)
    } else {
      this.value = value
    }
  }

  /**
   * @param {string} key
   * @param {any} value
   * @returns {void}
   */
  add (key, value) {
    const length = key.length
    if (length === 0) {
      throw new TypeError('Unreachable')
    }

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Audit the headers object you pass and strip/replace any character above 0x7F in field names.
  2. Validate dynamic header names against the RFC 7230 token grammar: /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/.
  3. If you need Unicode metadata, put it in the header VALUE (which allows obs-text), never the field name.
  4. Save source/config files as UTF-8 and re-check for accidental mojibake.

Example fix

// before
const h = new Headers({ 'X-Custöm': 'val' })
// after
const h = new Headers({ 'X-Custom': 'val' })
Defensive patterns

Strategy: validation

Validate before calling

const HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/
function safeHeaderName(name) {
  if (typeof name !== 'string' || !HEADER_NAME_RE.test(name)) {
    throw new TypeError(`Invalid header name: ${String(name)}`)
  }
  return name
}

Type guard

function isValidHeaderName(name) {
  return typeof name === 'string' && /^[A-Za-z0-9!#$%&'*+\-.^_`|~]+$/.test(name)
}

Prevention

When it happens

Trigger: Constructing a Headers instance, or supplying a headers init object to fetch()/request, whose property name contains a non-ASCII character (e.g. 'X-Ünïcödé'), or inserting such a key into any structure backed by TstNode.

Common situations: Header names read from a Latin-1/UTF-8 config file without sanitization; copy/paste from docs introducing invisible non-ASCII characters; tooling that auto-generates header names from free-form input.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/7b4220ec6ce75b57. Report an issue: GitHub.