{"record":{"id":"b0eb802bf3b17fb6","repo":"denoland/deno","slug":"err-invalid-char-b0eb80","errorCode":"ERR_INVALID_CHAR","errorMessage":"Invalid character in header content [\"Link\"]","messagePattern":"Invalid character in header content \\[\"Link\"\\]","errorType":"exception","errorClass":"NodeTypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_server.js","lineNumber":473,"sourceCode":"  hints,\n  cb,\n) {\n  let head = \"HTTP/1.1 103 Early Hints\\r\\n\";\n\n  validateObject(hints, \"hints\");\n\n  if (hints.link === null || hints.link === undefined) {\n    return;\n  }\n\n  const link = validateLinkHeaderValue(hints.link);\n\n  if (link.length === 0) {\n    return;\n  }\n\n  if (checkInvalidHeaderChar(link)) {\n    throw new ERR_INVALID_CHAR(\"header content\", \"Link\");\n  }\n\n  head += \"Link: \" + link + \"\\r\\n\";\n\n  const keys = ObjectKeys(hints);\n  for (let i = 0; i < keys.length; i++) {\n    const key = keys[i];\n    if (key !== \"link\") {\n      validateHeaderName(key);\n      const value = hints[key];\n      validateHeaderValue(key, value);\n      head += key + \": \" + value + \"\\r\\n\";\n    }\n  }\n\n  head += \"\\r\\n\";\n\n  this._writeRaw(head, \"ascii\", cb);","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_http_server.js#L455-L491","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nres.writeEarlyHints({ link: `<${userUrl}>; rel=preload` }); // userUrl may contain CRLF\n\n// after\nconst safe = userUrl.replace(/[\\r\\n\\x00-\\x1f\\x7f]/g, '');\nres.writeEarlyHints({ link: `<${encodeURI(safe)}>; rel=preload` });","handlingStrategy":"validation","validationCode":"const HEADER_VALUE_RE = /^[\\t\\x20-\\x7e\\x80-\\xff]*$/;\nfunction safeLinkValue(link) {\n  const v = Array.isArray(link) ? link.join(', ') : String(link);\n  return v.replace(/[\\r\\n\\x00-\\x1f\\x7f]/g, '');\n}\nconst link = safeLinkValue(hints.link);\nif (HEADER_VALUE_RE.test(link)) {\n  res.writeEarlyHints({ link });\n}","typeGuard":null,"tryCatchPattern":"try {\n  res.writeEarlyHints(hints);\n} catch (e) {\n  if (e.code === 'ERR_INVALID_CHAR') {\n    // drop the malformed hint and keep serving the normal response\n  } else throw e;\n}","preventionTips":["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"],"tags":["http","security","early-hints","header-injection","node-compat"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}