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
- Sanitize the link value: strip CR/LF and control characters before passing it
- URL-encode non-ASCII characters in the URL portion of each link
- Build links as '<https://example.com/a.css>; rel=preload' entries joined with ', '
- 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
- Never place raw request-derived data into headers without stripping CR/LF
- Centralize a sanitizeHeaderValue() helper used by every header write
- Add tests with CRLF-laden inputs to all hint/header paths
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ERR_INVALID_CHAR
- ERR_HTTP_HEADERS_SENT
- Invalid header value: "${value}"
- Unsupported 'alpnProtocols' option provided. 'h2' and 'http/
- ERR_INVALID_ARG_TYPE
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/b0eb802bf3b17fb6.
Report an issue: GitHub.