denoland/deno · error · NodeTypeError

ERR_INVALID_CHAR

ERR_INVALID_CHAR

Error message

Invalid character in header content ["Link"]

What it means

While building early-hints output, the polyfill normalizes options.link through validateLinkHeaderValue and then runs checkInvalidHeaderChar on the result before writing the 'Link:' header. Bytes that are illegal in an HTTP header value (CR, LF, other control characters, or bytes outside the valid header range) make it throw ERR_INVALID_CHAR('header content', 'Link'). This is the header-injection guard for the Link hint.

Source

Thrown at ext/node/polyfills/_http_server.js:473

  hints,
  cb,
) {
  let head = "HTTP/1.1 103 Early Hints\r\n";

  validateObject(hints, "hints");

  if (hints.link === null || hints.link === undefined) {
    return;
  }

  const link = validateLinkHeaderValue(hints.link);

  if (link.length === 0) {
    return;
  }

  if (checkInvalidHeaderChar(link)) {
    throw new ERR_INVALID_CHAR("header content", "Link");
  }

  head += "Link: " + link + "\r\n";

  const keys = ObjectKeys(hints);
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    if (key !== "link") {
      validateHeaderName(key);
      const value = hints[key];
      validateHeaderValue(key, value);
      head += key + ": " + value + "\r\n";
    }
  }

  head += "\r\n";

  this._writeRaw(head, "ascii", cb);

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Sanitize the link value: strip CR/LF and control characters before passing it
  2. URL-encode non-ASCII characters in the URL portion of each link
  3. Build links as '<https://example.com/a.css>; rel=preload' entries joined with ', '
  4. Reject or truncate untrusted link input at the API boundary

Example fix

// before
res.writeEarlyHints({ link: `<${userUrl}>; rel=preload` }); // userUrl may contain CRLF

// after
const safe = userUrl.replace(/[\r\n\x00-\x1f\x7f]/g, '');
res.writeEarlyHints({ link: `<${encodeURI(safe)}>; rel=preload` });
Defensive patterns

Strategy: validation

Validate before calling

const HEADER_VALUE_RE = /^[\t\x20-\x7e\x80-\xff]*$/;
function safeLinkValue(link) {
  const v = Array.isArray(link) ? link.join(', ') : String(link);
  return v.replace(/[\r\n\x00-\x1f\x7f]/g, '');
}
const link = safeLinkValue(hints.link);
if (HEADER_VALUE_RE.test(link)) {
  res.writeEarlyHints({ link });
}

Try / catch

try {
  res.writeEarlyHints(hints);
} catch (e) {
  if (e.code === 'ERR_INVALID_CHAR') {
    // drop the malformed hint and keep serving the normal response
  } else throw e;
}

Prevention

When it happens

Trigger: res.writeEarlyHints({ link: value }) where value (after normalization) contains a newline or other invalid header byte: user-supplied URLs with embedded CRLF, raw multi-byte/non-Latin-1 text, or control characters in the link value.

Common situations: Early hints whose URLs come from request input, a CMS, or a database; joining link lists with newline separators; localized text pasted into a Link value; header-injection attempts that reach the hint path.

Understand the failure class

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/b0eb802bf3b17fb6. Report an issue: GitHub.