{"record":{"id":"ccbf41acff2ba0e3","repo":"denoland/deno","slug":"err-invalid-http-token-ccbf41","errorCode":"ERR_INVALID_HTTP_TOKEN","errorMessage":"Header name must be a valid HTTP token [\"${key}\"]","messagePattern":"Header name must be a valid HTTP token \\[\"(.+?)\"\\]","errorType":"validation","errorClass":"NodeTypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/http2/util.ts","lineNumber":869,"sourceCode":"        throw new ERR_HTTP2_HEADER_SINGLE_VALUE(key);\n      }\n      singles.add(key);\n    }\n    const flags = ArrayPrototypeIncludes(neverIndex, key)\n      ? kNeverIndexFlag\n      : kNoHeaderFlags;\n    if (key[0] === \":\") {\n      const err = assertValuePseudoHeader(key);\n      if (err !== undefined) {\n        throw err;\n      }\n      value = escapeNgHeaderValueZeroBytes(value);\n      pseudoHeaders += `${key}\\0${value}\\0${flags}`;\n      count++;\n      return;\n    }\n    if (!checkIsHttpToken(key)) {\n      throw new ERR_INVALID_HTTP_TOKEN(\"Header name\", key);\n    }\n    if (isIllegalConnectionSpecificHeader(key, value)) {\n      throw new ERR_HTTP2_INVALID_CONNECTION_HEADERS(key);\n    }\n    if (isArray) {\n      for (let j = 0; j < value.length; ++j) {\n        const val = escapeNgHeaderValueZeroBytes(String(value[j]));\n        headers += `${key}\\0${val}\\0${flags}`;\n      }\n      count += value.length;\n      return;\n    }\n    value = escapeNgHeaderValueZeroBytes(value);\n    headers += `${key}\\0${value}\\0${flags}`;\n    count++;\n  }\n\n  if (ArrayIsArray(arrayOrMap)) {","sourceCodeStart":851,"sourceCodeEnd":887,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/http2/util.ts#L851-L887","documentation":"Non-pseudo header names must be valid HTTP tokens (RFC 7230 token grammar: visible ASCII, no separators/space/CTL). Before a header name is written into the HPACK string, checkIsHttpToken(key) runs and failure throws ERR_INVALID_HTTP_TOKEN with 'Header name' (util.ts:869). This also blocks header injection via CR/LF.","triggerScenarios":"Passing a header name containing a space, comma, colon-adjacent junk, non-ASCII, or control characters, e.g. { 'x custom': 'v' }, { 'café': 'v' }, or user input used directly as a header name in http2session.request()/stream.respond(). Note ':'-prefixed keys take the pseudo-header path, not this one.","commonSituations":"Using raw user/DB values as header names (x-user-${username}); fat-fingered names with spaces or underscores vs dashes confusion is fine but spaces are not; truncated mojibake strings; security scans probing CRLF injection (\"value\\r\\nX-Evil: 1\" used as a name).","solutions":["Validate/normalize header names with an HTTP-token regex (e.g. /^[!#$%&'*+.^_|~A-Za-z0-9-]+$/) before building headers.","Reject or sanitize user-supplied names: strip non-token characters or reject outright — do not silently forward.","Keep header names in a constants module instead of interpolating dynamic strings."],"exampleFix":"// before\nstream.respond({ ':status': 200, [`x-tenant-${tenantName}`]: '1' }); // tenantName = \"acme corp\"\n\n// after\nconst TOKEN = /^[!#$%&'*+.^_|~A-Za-z0-9-]+$/;\nconst name = `x-tenant-${tenantName}`;\nif (!TOKEN.test(name)) throw new Error(`bad header name: ${name}`);\nstream.respond({ ':status': 200, [name]: '1' });","handlingStrategy":"type-guard","validationCode":"const TOKEN_RE = /^[!#$%&'*+.^_|~A-Za-z0-9-]+$/;\nconst isValidHeaderName = (name) => typeof name === 'string' && name.length > 0 && TOKEN_RE.test(name);","typeGuard":"function isHttpToken(name: string): boolean {\n  return /^[!#$%&'*+.^_|~A-Za-z0-9-]+$/.test(name);\n}","tryCatchPattern":"try { stream.respond(h); } catch (e) { if (e.code === 'ERR_INVALID_HTTP_TOKEN') { /* find the name in e.message, sanitize or drop, rebuild h */ } throw e; }","preventionTips":["Never build header names from unfiltered user input","Keep header names in a constants module"],"tags":["http2","headers","validation","security","node-compat"],"backgroundTag":"invalid-header-name","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}